Linux-Bluetooth Archive on lore.kernel.org
 help / Atom feed
* [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus
@ 2018-12-28 22:07 Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 01/26] mesh: Structural changes for mesh Brian Gix
                   ` (25 more replies)
  0 siblings, 26 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

The Mesh Daemon has been largely rewritten.

It matches the API in doc/mesh-api.txt, and is now accessible from non-
privledged DBus clients.  The commandline version has been temporarily
removed, although it will probably be re-added in the future. The dBus
privledges need to be adjusted to allow for non-privledged access to
Daemon interfaces.  We will shortly also be adding a Mesh Specific README
and TODO file, but in the meantime, contact Brian and/or Inga with any
questions.

v6 -- Address Marcel's v5 comments, including cleanup, and unstacking
include files.  There was also some compression of commits, where little
was being actually done.  Other big difference with patch set is a more
verbose explanation of non-trivial commits, particularily where functionality
was removed (or deleted) in favor of rewrites in other (mostly new) files.
This is especially true of Provisioning which has been largely rewritten for
both readability and for focus.

Tested on TIP of upstream/master as of 28-Dec-2018, on Fedora 27, 28 and 29,
with both ./bootstrap-configure, and distcheck.

Brian Gix (10):
  mesh: Structural changes for mesh
  mesh: Rewrite Network layer for multiple nodes
  mesh: Direction agnostic PB-ADV implementation
  mesh: Acceptor side provisioning implementation
  mesh: Initiator side provisioning implementation
  mesh: Rewrite Controler interface for full init
  mesh: Unchanged variables set to const
  mesh: re-arrange provisioning for DBus API
  mesh: restructure I/O for multiple nodes
  mesh: Clean-up Comment style

Inga Stotland (16):
  mesh: Utilities for DBus support
  mesh: Internal errors
  mesh: Rewrite storage for Multiple Nodes
  mesh: Rewrite Node handling for multiple nodes
  mesh: Hex-String manipulation, and debug logging
  mesh: Re-architect for DBus API
  mesh: Multi node Config Server model
  mesh: Restructure DB to support multiple nodes
  mesh: Restructure model services for multiple nodes
  mesh: DBUS interface for Provisioning Agent
  mesh: restructure App Key storage
  mesh: Update for DBus API and multi-node support
  mesh: Add default location for Mesh Node storage
  mesh: Sample Provisioning Agent
  mesh: Sample On/Off Client and Server
  mesh: Sample Mesh Joiner (provision acceptor)

 .gitignore                   |    1 -
 Makefile.mesh                |   19 +-
 configure.ac                 |    3 +
 mesh/agent.c                 |  665 +++++++++++++++++----
 mesh/agent.h                 |   69 ++-
 mesh/appkey.c                |   19 +-
 mesh/appkey.h                |    1 -
 mesh/btmesh.c                |  181 ------
 mesh/cfgmod-server.c         |  179 +++---
 mesh/cfgmod.h                |    3 +-
 mesh/config/composition.json |   44 --
 mesh/dbus.c                  |  151 +++++
 mesh/dbus.h                  |   33 ++
 mesh/display.c               |   64 --
 mesh/error.h                 |   34 ++
 mesh/friend.c                |   76 +--
 mesh/main.c                  |   75 ++-
 mesh/mesh-db.c               |  456 +++++++++-----
 mesh/mesh-db.h               |    9 +-
 mesh/mesh-io-api.h           |    2 +-
 mesh/mesh-io-generic.c       |  149 ++++-
 mesh/mesh-io.c               |    3 +-
 mesh/mesh-io.h               |    3 +-
 mesh/mesh.c                  |  626 +++++++++++++++-----
 mesh/mesh.h                  |   28 +-
 mesh/model.c                 |  813 +++++++++++++++++--------
 mesh/model.h                 |   69 ++-
 mesh/net.c                   |  294 +++------
 mesh/net.h                   |   34 +-
 mesh/node.c                  | 1340 +++++++++++++++++++++++++++++++++++-------
 mesh/node.h                  |   43 +-
 mesh/pb-adv.c                |  444 ++++++++++++++
 mesh/{display.h => pb-adv.h} |   13 +-
 mesh/prov-acceptor.c         |  684 +++++++++++++++++++++
 mesh/prov-initiator.c        |  641 ++++++++++++++++++++
 mesh/prov.c                  |  722 -----------------------
 mesh/prov.h                  |   14 +-
 mesh/provision.c             | 1162 ------------------------------------
 mesh/provision.h             |  112 +++-
 mesh/storage.c               |  571 ++++++++----------
 mesh/storage.h               |   38 +-
 mesh/util.c                  |   27 +-
 mesh/util.h                  |    2 +-
 test/agent.py                |   40 ++
 test/example-onoff-client    |  288 +++++++++
 test/example-onoff-server    |  365 ++++++++++++
 test/test-join               |  408 +++++++++++++
 47 files changed, 7103 insertions(+), 3914 deletions(-)
 delete mode 100644 mesh/btmesh.c
 delete mode 100644 mesh/config/composition.json
 create mode 100644 mesh/dbus.c
 create mode 100644 mesh/dbus.h
 delete mode 100644 mesh/display.c
 create mode 100644 mesh/error.h
 create mode 100644 mesh/pb-adv.c
 rename mesh/{display.h => pb-adv.h} (71%)
 create mode 100644 mesh/prov-acceptor.c
 create mode 100644 mesh/prov-initiator.c
 delete mode 100644 mesh/prov.c
 delete mode 100644 mesh/provision.c
 create mode 100755 test/agent.py
 create mode 100644 test/example-onoff-client
 create mode 100644 test/example-onoff-server
 create mode 100644 test/test-join

-- 
2.14.5


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

* [PATCH BlueZ v6 01/26] mesh: Structural changes for mesh
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 02/26] mesh: Utilities for DBus support Brian Gix
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

btmesh.c: (Deleted) Contains no unique functionality
display.[ch]: retained functionality moved to util.c
prov.c: PB-ADV functionaility retained in pb-adv.c
proivision.c: Retained functionality split between
              prov-acceptor.c and prov-initiator.c
---
 .gitignore                   |    1 -
 Makefile.mesh                |   19 +-
 mesh/btmesh.c                |  181 -------
 mesh/config/composition.json |   44 --
 mesh/display.c               |   64 ---
 mesh/display.h               |   28 -
 mesh/prov.c                  |  722 --------------------------
 mesh/provision.c             | 1162 ------------------------------------------
 8 files changed, 6 insertions(+), 2215 deletions(-)
 delete mode 100644 mesh/btmesh.c
 delete mode 100644 mesh/config/composition.json
 delete mode 100644 mesh/display.c
 delete mode 100644 mesh/display.h
 delete mode 100644 mesh/prov.c
 delete mode 100644 mesh/provision.c

diff --git a/.gitignore b/.gitignore
index 24587305a..d145ccdcd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -130,7 +130,6 @@ emulator/hfp
 client/bluetoothctl
 tools/meshctl
 mesh/meshd
-mesh/btmesh
 
 src/bluetoothd.8
 src/bluetooth.service
diff --git a/Makefile.mesh b/Makefile.mesh
index 8b5af4cf6..ea6c5e939 100644
--- a/Makefile.mesh
+++ b/Makefile.mesh
@@ -3,35 +3,28 @@ if MESH
 mesh_sources = mesh/mesh.h mesh/mesh.c \
 				mesh/net_keys.h mesh/net_keys.c \
 				mesh/mesh-io.h mesh/mesh-io.c \
-				mesh/mesh-io-api.h \
+				mesh/error.h mesh/mesh-io-api.h \
 				mesh/mesh-io-generic.h \
 				mesh/mesh-io-generic.c \
 				mesh/storage.h mesh/storage.c \
 				mesh/net.h mesh/net.c \
-				mesh/display.h mesh/display.c \
 				mesh/crypto.h mesh/crypto.c \
 				mesh/friend.h mesh/friend.c \
 				mesh/appkey.h mesh/appkey.c \
 				mesh/node.h mesh/node.c \
-				mesh/prov.h mesh/prov.c \
-				mesh/provision.h mesh/provision.c \
+				mesh/provision.h mesh/prov.h \
 				mesh/model.h mesh/model.c \
 				mesh/cfgmod.h mesh/cfgmod-server.c \
 				mesh/mesh-db.h mesh/mesh-db.c \
 				mesh/util.h mesh/util.c \
+				mesh/dbus.h mesh/dbus.c \
+				mesh/agent.h mesh/agent.c \
+				mesh/prov-acceptor.c mesh/prov-initiator.c \
+				mesh/pb-adv.h mesh/pb-adv.c \
 				mesh/mesh-defs.h
 libexec_PROGRAMS += mesh/meshd
 
 mesh_meshd_SOURCES = $(mesh_sources) mesh/main.c
 mesh_meshd_LDADD = src/libshared-ell.la $(ell_ldadd) -ljson-c
 mesh_meshd_DEPENDENCIES = $(ell_dependencies) src/libshared-ell.la
-
-noinst_PROGRAMS += mesh/btmesh
-
-mesh_btmesh_SOURCES = $(mesh_sources) mesh/agent.h \
-						mesh/agent.c \
-						mesh/btmesh.c
-mesh_btmesh_LDADD = src/libshared-mainloop.la $(ell_ldadd) -lreadline -ljson-c
-mesh_btmesh_DEPENDENCIES = $(ell_dependencies)
-
 endif
diff --git a/mesh/btmesh.c b/mesh/btmesh.c
deleted file mode 100644
index 5b724239c..000000000
--- a/mesh/btmesh.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- *
- *  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
-
-#define _GNU_SOURCE
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <ell/ell.h>
-
-#include "src/shared/shell.h"
-#include "src/shared/mainloop.h"
-
-#include "mesh/mesh.h"
-#include "mesh/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) {
-		l_info("Controller index is required");
-		goto fail;
-	}
-
-	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_new(index, config_option);
-	if (!mesh) {
-		l_info("Failed to create mesh\n");
-		goto fail;
-	}
-
-	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);
-	mesh_cleanup();
-	l_main_exit();
-	return status;
-
-fail:
-	bt_shell_cleanup();
-	return EXIT_FAILURE;
-}
diff --git a/mesh/config/composition.json b/mesh/config/composition.json
deleted file mode 100644
index 20c0d0c3a..000000000
--- a/mesh/config/composition.json
+++ /dev/null
@@ -1,44 +0,0 @@
-{
-  "$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
-  },
-   }
-}
diff --git a/mesh/display.c b/mesh/display.c
deleted file mode 100644
index 8238ae849..000000000
--- a/mesh/display.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *
- *  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 <stdio.h>
-#include <unistd.h>
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <ell/ell.h>
-
-#include "display.h"
-
-static unsigned int cached_num_columns;
-
-unsigned int num_columns(void)
-{
-	struct winsize ws;
-
-	if (cached_num_columns > 0)
-		return cached_num_columns;
-
-	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0 || ws.ws_col == 0)
-		cached_num_columns = 80;
-	else
-		cached_num_columns = ws.ws_col;
-
-	return cached_num_columns;
-}
-
-void print_packet(const char *label, const void *data, uint16_t size)
-{
-	struct timeval pkt_time;
-
-	gettimeofday(&pkt_time, NULL);
-
-	if (size > 0) {
-		char *str;
-
-		str = l_util_hexstring(data, size);
-		l_debug("%05d.%03d %s: %s",
-				(uint32_t) pkt_time.tv_sec % 100000,
-				(uint32_t) pkt_time.tv_usec/1000, label, str);
-		l_free(str);
-	} else
-		l_debug("%05d.%03d %s: empty",
-				(uint32_t) pkt_time.tv_sec % 100000,
-				(uint32_t) pkt_time.tv_usec/1000, label);
-}
diff --git a/mesh/display.h b/mesh/display.h
deleted file mode 100644
index f5d1f7f79..000000000
--- a/mesh/display.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- *
- *  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/mesh/prov.c b/mesh/prov.c
deleted file mode 100644
index 45ced404c..000000000
--- a/mesh/prov.c
+++ /dev/null
@@ -1,722 +0,0 @@
-/*
- *
- *  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 "mesh/mesh-defs.h"
-
-#include "mesh/mesh-io.h"
-#include "mesh/display.h"
-#include "mesh/crypto.h"
-#include "mesh/net.h"
-#include "mesh/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/mesh/provision.c b/mesh/provision.c
deleted file mode 100644
index 17422ce0a..000000000
--- a/mesh/provision.c
+++ /dev/null
@@ -1,1162 +0,0 @@
-/*
- *
- *  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 "mesh/mesh-defs.h"
-#include "src/shared/ecc.h"
-
-#include "mesh/display.h"
-#include "mesh/net_keys.h"
-#include "mesh/crypto.h"
-#include "mesh/net.h"
-#include "mesh/prov.h"
-#include "mesh/provision.h"
-#include "mesh/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;
-	uint32_t net_key_id;
-	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, &net_key_id);
-	net_key_retrieve(net_key_id, 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.5


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

* [PATCH BlueZ v6 02/26] mesh: Utilities for DBus support
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 01/26] mesh: Structural changes for mesh Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 03/26] mesh: Internal errors Brian Gix
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

These files implement the BlueZ mesh DBus API as described
in doc/mesh-api.txt
---
 mesh/dbus.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 mesh/dbus.h |  33 +++++++++++++
 2 files changed, 184 insertions(+)
 create mode 100644 mesh/dbus.c
 create mode 100644 mesh/dbus.h

diff --git a/mesh/dbus.c b/mesh/dbus.c
new file mode 100644
index 000000000..3b2e2aa81
--- /dev/null
+++ b/mesh/dbus.c
@@ -0,0 +1,151 @@
+/*
+ *
+ *  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 <json-c/json.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/mgmt.h"
+
+#include "mesh/mesh-defs.h"
+#include "mesh/mesh-io.h"
+#include "mesh/node.h"
+#include "mesh/net.h"
+#include "mesh/storage.h"
+#include "mesh/cfgmod.h"
+#include "mesh/model.h"
+#include "mesh/mesh.h"
+#include "mesh/error.h"
+#include "mesh/dbus.h"
+
+static struct l_dbus *dbus;
+
+struct error_entry {
+	const char *dbus_err;
+	const char *default_desc;
+};
+
+/*
+ * Important: The entries in this table follow the order of
+ * enumerated values in mesh_error (file error.h)
+ */
+static struct error_entry const error_table[] =
+{
+	{ NULL, NULL },
+	{ ERROR_INTERFACE ".Failed", "Operation failed" },
+	{ ERROR_INTERFACE ".NotAuthorized", "Permission denied"},
+	{ ERROR_INTERFACE ".NotFound", "Object not found"},
+	{ ERROR_INTERFACE ".InvalidArgs", "Invalid arguments"},
+	{ ERROR_INTERFACE ".InProgress", "Already in progress"},
+	{ ERROR_INTERFACE ".AlreadyExists", "Already exists"},
+	{ ERROR_INTERFACE ".DoesNotExist", "Does not exist"},
+	{ ERROR_INTERFACE ".Canceled", "Operation canceled"}
+};
+
+struct l_dbus_message *dbus_error(struct l_dbus_message *msg, int err,
+							const char *description)
+{
+	int array_len = L_ARRAY_SIZE(error_table);
+
+	/* Default to ".Failed" */
+	if (!err || err >= array_len)
+		err = MESH_ERROR_FAILED;
+
+	if (description)
+		return l_dbus_message_new_error(msg,
+				error_table[err].dbus_err,
+				"%s", description);
+	else
+		return l_dbus_message_new_error(msg,
+				error_table[err].dbus_err,
+				"%s", error_table[err].default_desc);
+}
+
+struct l_dbus *dbus_get_bus(void)
+{
+	return dbus;
+}
+
+bool dbus_init(struct l_dbus *bus)
+{
+	/* Network interface */
+	if (!mesh_dbus_init(bus))
+		return false;
+
+	/* Node interface */
+	if (!node_dbus_init(bus))
+		return false;
+
+	dbus = bus;
+
+	return true;
+}
+
+bool dbus_match_interface(struct l_dbus_message_iter *interfaces,
+							const char *match)
+{
+	const char *interface;
+	struct l_dbus_message_iter properties;
+
+	while (l_dbus_message_iter_next_entry(interfaces, &interface,
+								&properties)) {
+		if (!strcmp(match, interface))
+			return true;
+	}
+
+	return false;
+}
+
+bool dbus_append_byte_array(struct l_dbus_message_builder *builder,
+						const uint8_t *data, int len)
+{
+	int i;
+
+	if (!l_dbus_message_builder_enter_array(builder, "y"))
+		return false;
+
+	for (i = 0; i < len; i++)
+		if (!l_dbus_message_builder_append_basic(builder, 'y',
+				data + i))
+			return false;
+
+	if (!l_dbus_message_builder_leave_array(builder))
+		return false;
+
+	return true;
+}
+
+void dbus_append_dict_entry_basic(struct l_dbus_message_builder *builder,
+					const char *key, const char *signature,
+					const void *data)
+{
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', key);
+	l_dbus_message_builder_enter_variant(builder, signature);
+	l_dbus_message_builder_append_basic(builder, signature[0], data);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+}
diff --git a/mesh/dbus.h b/mesh/dbus.h
new file mode 100644
index 000000000..879649452
--- /dev/null
+++ b/mesh/dbus.h
@@ -0,0 +1,33 @@
+/*
+ *
+ *  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 BLUEZ_MESH_PATH "/org/bluez/mesh"
+#define BLUEZ_MESH_SERVICE "org.bluez.mesh"
+
+bool dbus_init(struct l_dbus *dbus);
+struct l_dbus *dbus_get_bus(void);
+bool dbus_append_byte_array(struct l_dbus_message_builder *builder,
+						const uint8_t *data, int len);
+void dbus_append_dict_entry_basic(struct l_dbus_message_builder *builder,
+					const char *key, const char *signature,
+					const void *data);
+bool dbus_match_interface(struct l_dbus_message_iter *interfaces,
+							const char *match);
+struct l_dbus_message *dbus_error(struct l_dbus_message *msg, int err,
+						const char *description);
-- 
2.14.5


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

* [PATCH BlueZ v6 03/26] mesh: Internal errors
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 01/26] mesh: Structural changes for mesh Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 02/26] mesh: Utilities for DBus support Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 04/26] mesh: Rewrite storage for Multiple Nodes Brian Gix
                   ` (22 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

Error codes that map to specific faults that may occur while
using the Mesh DBus API.
---
 mesh/error.h | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 mesh/error.h

diff --git a/mesh/error.h b/mesh/error.h
new file mode 100644
index 000000000..a6aaa2fef
--- /dev/null
+++ b/mesh/error.h
@@ -0,0 +1,34 @@
+/*
+ *
+ *  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.
+ *
+ */
+
+/*
+ * Important: Changes in this table must be reflected in the
+ * the entries of error_table[] in dbus.c
+ */
+enum mesh_error {
+	MESH_ERROR_NONE,
+	MESH_ERROR_FAILED,
+	MESH_ERROR_NOT_AUTHORIZED,
+	MESH_ERROR_NOT_FOUND,
+	MESH_ERROR_INVALID_ARGS,
+	MESH_ERROR_BUSY,
+	MESH_ERROR_ALREADY_EXISTS,
+	MESH_ERROR_DOES_NOT_EXIST,
+	MESH_ERROR_CANCELED,
+};
-- 
2.14.5


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

* [PATCH BlueZ v6 04/26] mesh: Rewrite storage for Multiple Nodes
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (2 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 03/26] mesh: Internal errors Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 05/26] mesh: Rewrite Node handling for multiple nodes Brian Gix
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

Node storage now occurs in a privileged location, in the (default:
/var/lib/bluetooth/mesh) and is stored so that multiple nodes may
be handled by a single instance of the daemon. The node storage
files must be priviledged access only, to prevent unauthorized
access to the security material entrusted to each node.
---
 mesh/storage.c | 571 +++++++++++++++++++++++++--------------------------------
 mesh/storage.h |  38 ++--
 2 files changed, 267 insertions(+), 342 deletions(-)

diff --git a/mesh/storage.c b/mesh/storage.c
index 1f3b25886..57ae88b34 100644
--- a/mesh/storage.c
+++ b/mesh/storage.c
@@ -15,7 +15,6 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -27,6 +26,8 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <dirent.h>
+#include <libgen.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -42,39 +43,43 @@
 #include "mesh/net.h"
 #include "mesh/appkey.h"
 #include "mesh/model.h"
+#include "mesh/mesh-db.h"
 #include "mesh/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"
+struct write_info {
+	json_object *jnode;
+	const char *config_name;
+	void *user_data;
+	mesh_status_func_t cb;
+};
+
+static const char *storage_dir;
+static struct l_queue *node_ids;
 
-static bool read_local_node_cb(struct mesh_db_node *db_node, void *user_data)
+static bool simple_match(const void *a, const void *b)
 {
-	struct mesh_net *net = user_data;
-	struct mesh_node *node;
+	return a == b;
+}
+
+static bool read_node_cb(struct mesh_db_node *db_node, void *user_data)
+{
+	struct mesh_node *node = user_data;
+	struct mesh_net *net;
 	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)
+	if (!node_init_from_storage(node, db_node)) {
+		node_free(node);
 		return false;
+	}
 
-	mesh_net_local_node_set(net, node, db_node->provisioner);
+	net = node_get_net(node);
 	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)
@@ -103,6 +108,7 @@ static bool read_local_node_cb(struct mesh_db_node *db_node, void *user_data)
 	uuid = node_uuid_get(node);
 	if (uuid)
 		mesh_net_id_uuid_set(net, uuid);
+
 	return true;
 }
 
@@ -132,13 +138,14 @@ static bool read_app_keys_cb(uint16_t net_idx, uint16_t app_idx, uint8_t *key,
 	return appkey_key_init(net, net_idx, app_idx, key, new_key);
 }
 
-static bool parse_local_node(struct mesh_net *net, json_object *jnode)
+static bool parse_node(struct mesh_node *node, json_object *jnode)
 {
 	bool bvalue;
 	uint32_t iv_index;
 	uint8_t key_buf[16];
 	uint8_t cnt;
 	uint16_t interval;
+	struct mesh_net *net = node_get_net(node);
 
 	if (mesh_db_read_iv_index(jnode, &iv_index, &bvalue))
 		mesh_net_set_iv_index(net, iv_index, bvalue);
@@ -147,97 +154,35 @@ static bool parse_local_node(struct mesh_net *net, json_object *jnode)
 		mesh_net_transmit_params_set(net, cnt, interval);
 
 	/* Node composition/configuration info */
-	if (!mesh_db_read_node(jnode, read_local_node_cb, net))
+	if (!mesh_db_read_node(jnode, read_node_cb, node))
 		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))
+	if (!mesh_db_read_device_key(jnode, 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;
+	node_set_device_key(node, key_buf);
 
-	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);
+	mesh_db_read_app_keys(jnode, read_app_keys_cb, net);
 
 	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)
+static bool parse_config(char *in_file, char *out_file, uint16_t node_id)
 {
 	int fd;
 	char *str;
-	const char *out;
 	struct stat st;
 	ssize_t sz;
 	json_object *jnode = NULL;
 	bool result = false;
+	struct mesh_node *node;
 
-	if (!config_name)
-		return false;
+	l_info("Loading configuration from %s", in_file);
 
-	fd = open(config_name, O_RDONLY);
+	fd = open(in_file, O_RDONLY);
 	if (!fd)
 		return false;
 
@@ -254,7 +199,7 @@ static bool parse_config(struct mesh_net *net, const char *config_name,
 
 	sz = read(fd, str, st.st_size);
 	if (sz != st.st_size) {
-		l_error("Failed to read configuration file");
+		l_error("Failed to read configuration file %s", in_file);
 		goto done;
 	}
 
@@ -262,22 +207,19 @@ static bool parse_config(struct mesh_net *net, const char *config_name,
 	if (!jnode)
 		goto done;
 
-	mesh_net_jconfig_set(net, jnode);
+	node = node_new();
 
-	if (!unprovisioned)
-		result = parse_local_node(net, jnode);
-	else
-		result = parse_unprovisioned_device(net, jnode);
+	node_jconfig_set(node, jnode);
+	node_cfg_file_set(node, out_file);
+	node_id_set(node, node_id);
+
+	result = parse_node(node, jnode);
 
 	if (!result) {
-		storage_release(net);
-		goto done;
+		json_object_put(jnode);
+		node_free(node);
 	}
 
-	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)
@@ -286,166 +228,70 @@ done:
 	return result;
 }
 
-bool storage_parse_config(struct mesh_net *net, const char *config_name)
+bool storage_set_ttl(json_object *jnode, uint8_t ttl)
 {
-	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) {
-		size_t len = strlen(config_name) + 5;
-		char *bak = l_malloc(len);
-
-		/* Fall-back to Backup version */
-		strncpy(bak, config_name, len);
-		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,
+bool storage_set_relay(json_object *jnode, 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,
+bool storage_set_transmit_params(json_object *jnode, 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,
+bool storage_set_mode(json_object *jnode, 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,
+bool storage_model_bind(struct mesh_node *node, uint16_t addr, uint32_t mod_id,
 				uint16_t app_idx, bool unbind)
 {
 	json_object *jnode;
-	bool is_local;
+	int ele_idx;
+	bool is_vendor = (mod_id > 0xffff);
 
-	if (!net)
+	ele_idx = node_get_element_idx(node, addr);
+	if (ele_idx < 0)
 		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);
-	}
+	jnode = node_jconfig_get(node);
 
-	/* TODO: write remote node bindings to provisioner DB */
-	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);
 }
 
-bool storage_local_app_key_add(struct mesh_net *net, uint16_t net_idx,
+bool storage_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;
+	struct mesh_node *node = mesh_net_node_get(net);
 
-	if (!net)
-		return false;
-
-	jnode = mesh_net_jconfig_get(net);
+	jnode = node_jconfig_get(node);
 	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,
+bool storage_app_key_del(struct mesh_net *net, uint16_t net_idx,
 					uint16_t app_idx)
 {
 	json_object *jnode;
+	struct mesh_node *node = mesh_net_node_get(net);
 
-	if (!net)
-		return false;
-
-	jnode = mesh_net_jconfig_get(net);
+	jnode = node_jconfig_get(node);
 	if (!jnode)
 		return false;
 
@@ -453,116 +299,53 @@ bool storage_local_app_key_del(struct mesh_net *net, uint16_t net_idx,
 
 }
 
-bool storage_local_net_key_add(struct mesh_net *net, uint16_t net_idx,
+bool storage_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;
+	struct mesh_node *node = mesh_net_node_get(net);
+	json_object *jnode = node_jconfig_get(node);
 
 	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)
+bool storage_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;
+	struct mesh_node *node = mesh_net_node_get(net);
+	json_object *jnode = node_jconfig_get(node);
 
 	return mesh_db_net_key_del(jnode, net_idx);
 }
 
-bool storage_local_set_iv_index(struct mesh_net *net, uint32_t iv_index,
+bool storage_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;
+	struct mesh_node *node = mesh_net_node_get(net);
+	json_object *jnode = node_jconfig_get(node);
 
 	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)
+bool storage_write_sequence_number(struct mesh_net *net, uint32_t seq)
 {
-	json_object *jnode;
-	const char *cfg_file;
+	struct mesh_node *node = mesh_net_node_get(net);
+	json_object *jnode = node_jconfig_get(node);
 	bool result;
-
-	if (!net)
-		return false;
-
-	jnode = mesh_net_jconfig_get(net);
-	if (!jnode)
-		return false;
-
+	l_debug("");
 	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);
+	result = storage_save_config(node, false, NULL, NULL);
 
 	return result;
 }
 
-static bool save_config(struct mesh_net *net, const char *config_name)
+static bool save_config(json_object *jnode, 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);
@@ -581,13 +364,6 @@ static bool save_config(struct mesh_net *net, const char *config_name)
 	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;
@@ -603,7 +379,7 @@ static void idle_save_config(void *user_data)
 	remove(tmp);
 
 	l_debug("Storage-Wrote");
-	result = save_config(info->net, tmp);
+	result = save_config(info->jnode, tmp);
 
 	if (result) {
 		remove(bak);
@@ -621,48 +397,199 @@ static void idle_save_config(void *user_data)
 	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)
+bool storage_save_config(struct mesh_node *node, 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;
+	l_debug("");
+	info->jnode = node_jconfig_get(node);
+	info->config_name = node_cfg_file_get(node);
 	info->cb = cb;
 	info->user_data = user_data;
 
 	if (no_wait)
 		idle_save_config(info);
-	l_idle_oneshot(idle_save_config, info, NULL);
+	else
+		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)
+static int create_dir(const char *dirname)
 {
-	json_object *jnode;
+	struct stat st;
+	char dir[PATH_MAX + 1], *prev, *next;
+	int err;
 
-	jnode = mesh_net_jconfig_get(net);
-	if (!jnode)
+	err = stat(dirname, &st);
+	if (!err && S_ISREG(st.st_mode))
+		return 0;
+
+	memset(dir, 0, PATH_MAX + 1);
+	strcat(dir, "/");
+
+	prev = strchr(dirname, '/');
+
+	while (prev) {
+		next = strchr(prev + 1, '/');
+		if (!next)
+			break;
+
+		if (next - prev == 1) {
+			prev = next;
+			continue;
+		}
+
+		strncat(dir, prev + 1, next - prev);
+		mkdir(dir, 0755);
+
+		prev = next;
+	}
+
+	mkdir(dirname, 0755);
+
+	return 0;
+}
+
+bool storage_load_nodes(const char *dir_name)
+{
+	DIR *dir;
+	struct dirent *entry;
+
+	create_dir(dir_name);
+	dir = opendir(dir_name);
+	if (!dir) {
+		l_error("Failed to open mesh node storage directory: %s",
+								dir_name);
 		return false;
+	}
+
+	storage_dir = dir_name;
+	node_ids = l_queue_new();
+
+	while ((entry = readdir(dir)) != NULL) {
+		char name_buf[PATH_MAX];
+		char *filename;
+		uint32_t node_id;
+		size_t len;
+
+		if (entry->d_type != DT_DIR)
+			continue;
+
+		if (sscanf(entry->d_name, "%04x", &node_id) != 1)
+			continue;
+
+		snprintf(name_buf, PATH_MAX, "%s/%s/node.json", dir_name,
+								entry->d_name);
+
+		l_queue_push_tail(node_ids, L_UINT_TO_PTR(node_id));
+
+		len = strlen(name_buf);
+		filename = l_malloc(len + 1);
 
-	mesh_db_remove_property(jnode, "provision");
+		strncpy(filename, name_buf, len + 1);
 
-	return storage_save_config(net, config_name, false, cb, user_data);
+		if (parse_config(name_buf, filename, node_id))
+			continue;
+
+		/* Fall-back to Backup version */
+		snprintf(name_buf, PATH_MAX, "%s/%s/node.json.bak", dir_name,
+								entry->d_name);
+
+		if (parse_config(name_buf, filename, node_id)) {
+			remove(filename);
+			rename(name_buf, filename);
+			continue;
+		}
+
+		l_free(filename);
+	}
+
+	return true;
 }
 
-void storage_release(struct mesh_net *net)
+bool storage_create_node_config(struct mesh_node *node, void *data)
 {
+	struct mesh_db_node *db_node = data;
+	uint16_t node_id;
+	uint8_t num_tries = 0;
+	char name_buf[PATH_MAX];
+	char *filename;
 	json_object *jnode;
+	size_t len;
+
+	if (!storage_dir)
+		return false;
+
+	jnode = json_object_new_object();
+
+	if (!mesh_db_add_node(jnode, db_node))
+		return false;
+
+	do {
+		l_getrandom(&node_id, 2);
+		if (!l_queue_find(node_ids, simple_match,
+						L_UINT_TO_PTR(node_id)))
+			break;
+	} while (++num_tries < 10);
+
+	if (num_tries == 10)
+		l_error("Failed to generate unique node ID");
+
+	snprintf(name_buf, PATH_MAX, "%s/%04x", storage_dir, node_id);
+
+	/* Create a new directory and node.json file */
+	if (mkdir(name_buf, 0755) != 0)
+		goto fail;
 
-	jnode = mesh_net_jconfig_get(net);
+	len = strlen(name_buf) + strlen("/node.json") + 1;
+	filename = l_malloc(len);
+
+	snprintf(filename, len, "%s/node.json", name_buf);
+	l_debug("New node config %s", filename);
+
+	if (!save_config(jnode, filename)) {
+		l_free(filename);
+		goto fail;
+	}
+
+	node_jconfig_set(node, jnode);
+	node_cfg_file_set(node, filename);
+
+	return true;
+fail:
+	json_object_put(jnode);
+	return false;
+}
+
+/* Permanently remove node configuration */
+void storage_remove_node_config(struct mesh_node *node)
+{
+	char *cfgname;
+	struct json_object *jnode;
+	const char *dir_name;
+
+	jnode = node_jconfig_get(node);
 	if (jnode)
 		json_object_put(jnode);
+	node_jconfig_set(node, NULL);
+
+	cfgname = (char *) node_cfg_file_get(node);
+	if (!cfgname)
+		return;
+
+	l_debug("Delete node config file %s", cfgname);
+	remove(cfgname);
+
+	dir_name = dirname(cfgname);
+
+	l_debug("Delete directory %s", dir_name);
+	rmdir(dir_name);
 
-	mesh_net_jconfig_set(net, NULL);
+	l_free(cfgname);
+	node_cfg_file_set(node, NULL);
 }
diff --git a/mesh/storage.h b/mesh/storage.h
index 341932dba..91299f0a8 100644
--- a/mesh/storage.h
+++ b/mesh/storage.h
@@ -15,37 +15,35 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 struct mesh_net;
+struct mesh_node;
 
-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,
+bool storage_load_nodes(const char *dir);
+bool storage_create_node_config(struct mesh_node *node, void *db_node);
+void storage_remove_node_config(struct mesh_node *node);
+bool storage_save_config(struct mesh_node *node, bool no_wait,
 					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,
+bool storage_model_bind(struct mesh_node *node, 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,
+bool storage_set_ttl(json_object *jnode, uint8_t ttl);
+bool storage_set_relay(json_object *jnode, bool enable, uint8_t count,
 							uint8_t interval);
-bool storage_local_set_transmit_params(struct mesh_net *net, uint8_t count,
+bool storage_set_transmit_params(json_object *jnode, uint8_t count,
 							uint8_t interval);
-bool storage_local_set_mode(struct mesh_net *net, uint8_t mode,
+bool storage_set_mode(json_object *jnode, uint8_t mode,
 							const char *mode_name);
-bool storage_local_net_key_add(struct mesh_net *net, uint16_t net_idx,
+bool storage_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,
+bool storage_net_key_del(struct mesh_net *net, uint16_t net_idx);
+bool storage_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,
+bool storage_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 storage_write_sequence_number(struct mesh_net *net, uint32_t seq);
+bool storage_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);
+bool storage_set_device_key(struct mesh_node *node, uint8_t dev_key[16]);
+bool storage_set_unicast(struct mesh_node *node, uint16_t unicast);
-- 
2.14.5


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

* [PATCH BlueZ v6 05/26] mesh: Rewrite Node handling for multiple nodes
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (3 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 04/26] mesh: Rewrite storage for Multiple Nodes Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 06/26] mesh: Rewrite Network layer " Brian Gix
                   ` (20 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

The Node describes an addressable entity in the Mesh.  It has
a composition that described the capabilities and purpose of
the Node to remote Config Client nodes that are responsible
for entire mesh maintenance. I also maintains runtime storage of
the Encryption/Decryption keys, Subscriptions, and Publications
that the remote Config Client has set up.
---
 mesh/node.c | 1340 +++++++++++++++++++++++++++++++++++++++++++++++++----------
 mesh/node.h |   43 +-
 2 files changed, 1159 insertions(+), 224 deletions(-)

diff --git a/mesh/node.c b/mesh/node.c
index 501ab8eea..e921b72b7 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -15,28 +15,47 @@
  *  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 <sys/time.h>
 #include <ell/ell.h>
+#include <json-c/json.h>
 
 #include "mesh/mesh-defs.h"
 
 #include "mesh/mesh.h"
+#include "mesh/mesh-io.h"
 #include "mesh/net.h"
-#include "mesh/node.h"
+#include "mesh/mesh-db.h"
+#include "mesh/provision.h"
 #include "mesh/storage.h"
 #include "mesh/appkey.h"
 #include "mesh/model.h"
+#include "mesh/cfgmod.h"
+#include "mesh/util.h"
+#include "mesh/error.h"
+#include "mesh/dbus.h"
+#include "mesh/agent.h"
+#include "mesh/node.h"
 
 #define MIN_COMP_SIZE 14
 
+#define MESH_NODE_PATH_PREFIX "/node"
+#define MESH_ELEMENT_PATH_PREFIX "/ele"
+
+/* Default element location: unknown */
+#define DEFAULT_LOCATION 0x0000
+
+#define DEFAULT_CRPL 10
+#define DEFAULT_SEQUENCE_NUMBER 0
+
 struct node_element {
+	char *path;
 	struct l_queue *models;
 	uint16_t location;
 	uint8_t idx;
@@ -51,30 +70,45 @@ struct node_composition {
 
 struct mesh_node {
 	struct mesh_net *net;
-	struct l_queue *net_keys;
-	struct l_queue *app_keys;
 	struct l_queue *elements;
+	char *app_path;
+	char *owner;
+	char *path;
+	void *jconfig;
+	char *cfg_file;
+	uint32_t disc_watch;
 	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;
+	uint16_t id;
 	bool provisioner;
+	uint16_t primary;
 	struct node_composition *comp;
 	struct {
 		uint16_t interval;
 		uint8_t cnt;
 		uint8_t mode;
 	} relay;
+	uint8_t dev_uuid[16];
+	uint8_t dev_key[16];
+	uint8_t num_ele;
+	uint8_t ttl;
 	uint8_t lpn;
 	uint8_t proxy;
 	uint8_t friend;
 	uint8_t beacon;
 };
 
+struct attach_obj_request {
+	node_attach_ready_func_t cb;
+	struct mesh_node *node;
+};
+
+struct join_obj_request {
+	node_join_ready_func_t cb;
+	const uint8_t *uuid;
+};
+
 static struct l_queue *nodes;
 
 static bool match_node_unicast(const void *a, const void *b)
@@ -94,6 +128,14 @@ static bool match_device_uuid(const void *a, const void *b)
 	return (memcmp(node->dev_uuid, uuid, 16) == 0);
 }
 
+static bool match_token(const void *a, const void *b)
+{
+	const struct mesh_node *node = a;
+	const uint64_t *token = b;
+	const uint64_t tmp = l_get_u64(node->dev_key);
+	return *token == tmp;
+}
+
 static bool match_element_idx(const void *a, const void *b)
 {
 	const struct node_element *element = a;
@@ -102,17 +144,15 @@ static bool match_element_idx(const void *a, const void *b)
 	return (element->idx == index);
 }
 
-static bool match_key_idx(const void *a, const void *b)
+static bool match_element_path(const void *a, const void *b)
 {
-	return (L_PTR_TO_UINT(a) == L_PTR_TO_UINT(b));
-}
+	const struct node_element *element = a;
+	const char *path = 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);
+	if (!element->path)
+		return false;
 
-	return (mesh_model_get_model_id(model) == id);
+	return (!strcmp(element->path, path));
 }
 
 struct mesh_node *node_find_by_addr(uint16_t addr)
@@ -140,20 +180,30 @@ struct mesh_node *node_new(void)
 	struct mesh_node *node;
 
 	node = l_new(struct mesh_node, 1);
+	node->net = mesh_net_new(node);
 
-	if (!node)
-		return NULL;
+	if (!nodes)
+		nodes = l_queue_new();
 
 	l_queue_push_tail(nodes, node);
 
 	return node;
 }
 
+static void free_element_path(void *a, void *b)
+{
+	struct node_element *element = a;
+
+	l_free(element->path);
+	element->path = NULL;
+}
+
 static void element_free(void *data)
 {
 	struct node_element *element = data;
 
 	l_queue_destroy(element->models, mesh_model_free);
+	l_free(element->path);
 	l_free(element);
 }
 
@@ -161,13 +211,20 @@ 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);
+	/* Unregister io callbacks */
+	if(node->net)
+		mesh_net_detach(node->net);
+	mesh_net_free(node->net);
+
 	l_queue_destroy(node->elements, element_free);
 	l_free(node->comp);
+	l_free(node->app_path);
+	l_free(node->owner);
 
-	if (node->net)
-		mesh_net_unref(node->net);
+	if (node->path)
+		l_dbus_object_remove_interface(dbus_get_bus(), node->path,
+					MESH_NODE_INTERFACE);
+	l_free(node->path);
 
 	l_free(node);
 }
@@ -176,19 +233,18 @@ 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,
+static bool add_models(struct mesh_node *node, 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) {
@@ -196,7 +252,7 @@ static bool add_models(struct mesh_net *net, struct node_element *ele,
 		struct mesh_db_model *db_mod;
 
 		db_mod = entry->data;
-		mod = mesh_model_init(net, ele->idx, db_mod);
+		mod = mesh_model_setup(node, ele->idx, db_mod);
 		if (!mod)
 			return false;
 
@@ -206,6 +262,32 @@ static bool add_models(struct mesh_net *net, struct node_element *ele,
 	return true;
 }
 
+static void add_internal_model(struct mesh_node *node, uint32_t mod_id,
+								uint8_t ele_idx)
+{
+	struct node_element *ele;
+	struct mesh_model *mod;
+	struct mesh_db_model db_mod;
+
+	ele = l_queue_find(node->elements, match_element_idx,
+							L_UINT_TO_PTR(ele_idx));
+
+	if (!ele)
+		return;
+
+	memset(&db_mod, 0, sizeof(db_mod));
+	db_mod.id = mod_id;
+
+	mod = mesh_model_setup(node, ele_idx, &db_mod);
+	if (!mod)
+		return;
+
+	if (!ele->models)
+		ele->models = l_queue_new();
+
+	l_queue_push_tail(ele->models, mod);
+}
+
 static bool add_element(struct mesh_node *node, struct mesh_db_element *db_ele)
 {
 	struct node_element *ele;
@@ -217,7 +299,7 @@ static bool add_element(struct mesh_node *node, struct mesh_db_element *db_ele)
 	ele->idx = db_ele->index;
 	ele->location = db_ele->location;
 
-	if (!db_ele->models || !add_models(node->net, ele, db_ele))
+	if (!db_ele->models || !add_models(node, ele, db_ele))
 		return false;
 
 	l_queue_push_tail(node->elements, ele);
@@ -231,9 +313,6 @@ static bool add_elements(struct mesh_node *node, struct mesh_db_node *db_node)
 	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))
@@ -242,26 +321,12 @@ static bool add_elements(struct mesh_node *node, struct mesh_db_node *db_node)
 	return true;
 }
 
-struct mesh_node *node_create_from_storage(struct mesh_net *net,
-						struct mesh_db_node *db_node,
-								bool local)
+bool node_init_from_storage(struct mesh_node *node, void *data)
 {
-	struct mesh_node *node;
+	struct mesh_db_node *db_node = data;
 	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;
@@ -276,107 +341,77 @@ struct mesh_node *node_create_from_storage(struct mesh_net *net,
 	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);
+	l_debug("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;
-	}
+	if (num_ele > 0xff)
+		return false;
 
 	node->num_ele = num_ele;
-	if (num_ele != 0 && !add_elements(node, db_node)) {
-		node_free(node);
-		return NULL;
-	}
+	if (num_ele != 0 && !add_elements(node, db_node))
+		return false;
 
 	node->primary = db_node->unicast;
 
 	memcpy(node->dev_uuid, db_node->uuid, 16);
 
-	if (local)
-		node->net = mesh_net_ref(net);
+	/* Initialize configuration server model */
+	mesh_config_srv_init(node, PRIMARY_ELE_IDX);
 
-	return node;
+	return true;
 }
 
-void node_cleanup(struct mesh_net *net)
+void node_cleanup(void *data)
 {
-	struct mesh_node *node;
+	struct mesh_node *node = data;
+	struct mesh_net *net = node->net;
 
-	if (!net)
-		return;
-	node = mesh_net_local_node_get(net);
-	if (node)
-		node_free(node);
+	/* Save local node configuration */
+	if (node->cfg_file) {
 
-	l_queue_destroy(nodes, free_node_resources);
+		/* Preserve the last sequence number */
+		storage_write_sequence_number(net, mesh_net_get_seq_num(net));
 
+		if (storage_save_config(node, true, NULL, NULL))
+			l_info("Saved final config to %s", node->cfg_file);
+	}
+
+	if (node->disc_watch)
+		l_dbus_remove_watch(dbus_get_bus(), node->disc_watch);
+
+	free_node_resources(node);
 }
 
-bool node_is_provisioned(struct mesh_node *node)
+void node_cleanup_all(void)
 {
-	return (!IS_UNASSIGNED(node->primary));
+	l_queue_destroy(nodes, node_cleanup);
+	l_dbus_unregister_interface(dbus_get_bus(), MESH_NODE_INTERFACE);
 }
 
-bool node_net_key_delete(struct mesh_node *node, uint16_t idx)
+bool node_is_provisioned(struct mesh_node *node)
 {
-	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;
+	return (!IS_UNASSIGNED(node->primary));
 }
 
 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);
+		mesh_model_app_key_delete(node, 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;
 }
 
@@ -388,20 +423,9 @@ uint16_t node_get_primary(struct mesh_node *node)
 		return node->primary;
 }
 
-bool node_set_device_key(struct mesh_node *node, uint8_t key[16])
-
+void 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)
@@ -417,22 +441,6 @@ 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)
 {
@@ -458,31 +466,6 @@ struct l_queue *node_get_element_models(struct mesh_node *node,
 	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)
@@ -492,20 +475,16 @@ uint8_t node_default_ttl_get(struct mesh_node *node)
 
 bool node_default_ttl_set(struct mesh_node *node, uint8_t ttl)
 {
-	bool res, is_local;
+	bool res;
 
 	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);
+	res = storage_set_ttl(node->jconfig, ttl);
 
 	if (res) {
 		node->ttl = ttl;
-		if (is_local)
-			mesh_net_set_default_ttl(node->net, ttl);
+		mesh_net_set_default_ttl(node->net, ttl);
 	}
 
 	return res;
@@ -513,21 +492,13 @@ bool node_default_ttl_set(struct mesh_node *node, uint8_t ttl)
 
 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
@@ -541,7 +512,7 @@ bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)
 		if (elapsed < MIN_SEQ_CACHE_TIME) {
 			uint32_t ideal = node->seq_min_cache;
 
-			l_info("Old Seq Cache: %d", node->seq_min_cache);
+			l_debug("Old Seq Cache: %d", node->seq_min_cache);
 
 			ideal *= (MIN_SEQ_CACHE_TIME / elapsed);
 
@@ -550,14 +521,13 @@ bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)
 			else
 				node->seq_min_cache += MIN_SEQ_CACHE;
 
-			l_info("New Seq Cache: %d", node->seq_min_cache);
+			l_debug("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);
+	return storage_write_sequence_number(node->net, seq);
 }
 
 uint32_t node_get_sequence_number(struct mesh_node *node)
@@ -629,24 +599,19 @@ 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)
 {
-	bool res, is_local;
+	bool res;
 
 	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);
+	res = storage_set_relay(node->jconfig, 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);
+		mesh_net_set_relay_mode(node->net, enable, cnt, interval);
 	}
 
 	return res;
@@ -654,22 +619,18 @@ bool node_relay_mode_set(struct mesh_node *node, bool enable, uint8_t cnt,
 
 bool node_proxy_mode_set(struct mesh_node *node, bool enable)
 {
-	bool res, is_local;
+	bool res;
 	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");
+	res = storage_set_mode(node->jconfig, proxy, "proxy");
 
 	if (res) {
 		node->proxy = proxy;
-		if (is_local)
-			mesh_net_set_proxy_mode(node->net, enable);
+		mesh_net_set_proxy_mode(node->net, enable);
 	}
 
 	return res;
@@ -685,22 +646,18 @@ uint8_t node_proxy_mode_get(struct mesh_node *node)
 
 bool node_beacon_mode_set(struct mesh_node *node, bool enable)
 {
-	bool res, is_local;
+	bool res;
 	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");
+	res = storage_set_mode(node->jconfig, beacon, "beacon");
 
 	if (res) {
 		node->beacon = beacon;
-		if (is_local)
-			mesh_net_set_beacon_mode(node->net, enable);
+		mesh_net_set_beacon_mode(node->net, enable);
 	}
 
 	return res;
@@ -716,22 +673,18 @@ uint8_t node_beacon_mode_get(struct mesh_node *node)
 
 bool node_friend_mode_set(struct mesh_node *node, bool enable)
 {
-	bool res, is_local;
+	bool res;
 	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");
+	res = storage_set_mode(node->jconfig, friend, "friend");
 
 	if (res) {
 		node->friend = friend;
-		if (is_local)
-			mesh_net_set_friend_mode(node->net, enable);
+		mesh_net_set_friend_mode(node->net, enable);
 	}
 
 	return res;
@@ -788,7 +741,7 @@ uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz)
 		/* 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;
 
@@ -805,7 +758,7 @@ uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz)
 			mod_id = mesh_model_get_model_id(
 					(const struct mesh_model *) mod);
 
-			if ((mod_id >> 16) == 0xffff) {
+			if ((mod_id & VENDOR_ID_MASK) == VENDOR_ID_MASK) {
 				if (n + 2 > sz)
 					goto element_done;
 
@@ -849,3 +802,970 @@ element_done:
 
 	return n;
 }
+
+
+#define MIN_COMPOSITION_LEN 16
+
+bool node_parse_composition(struct mesh_node *node, uint8_t *data,
+								uint16_t len)
+{
+	struct node_composition *comp;
+	uint16_t features;
+	uint8_t num_ele;
+	bool mode;
+
+	if (!len)
+		return false;
+
+	/* Skip page -- We only support Page Zero */
+	data++;
+	len--;
+
+	if (len < MIN_COMPOSITION_LEN)
+		return false;
+
+	comp = l_new(struct node_composition, 1);
+	if (!comp)
+		return false;
+
+	node->elements = l_queue_new();
+	if (!node->elements) {
+		l_free(comp);
+		return false;
+	}
+
+	node->comp = l_new(struct node_composition, 1);
+	comp->cid = l_get_le16(&data[0]);
+	comp->pid = l_get_le16(&data[2]);
+	comp->vid = l_get_le16(&data[4]);
+	comp->crpl = l_get_le16(&data[6]);
+	features = l_get_le16(&data[8]);
+	data += 10;
+	len -= 10;
+
+	mode = !!(features & FEATURE_PROXY);
+	node->proxy = mode ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED;
+
+	mode = !!(features & FEATURE_LPN);
+	node->lpn = mode ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED;
+
+	mode = !!(features & FEATURE_FRIEND);
+	node->friend = mode ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED;
+
+	mode = !!(features & FEATURE_RELAY);
+	node->relay.mode = mode ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED;
+
+	num_ele = 0;
+
+	do {
+		uint8_t m, v;
+		uint16_t mod_id;
+		uint16_t vendor_id;
+		struct node_element *ele;
+		struct mesh_model *mod;
+
+		ele = l_new(struct node_element, 1);
+		if (!ele)
+			return false;
+
+		ele->idx = num_ele;
+		ele->location = l_get_le16(data);
+		len -= 2;
+		data += 2;
+
+		m = *data++;
+		v = *data++;
+		len -= 2;
+
+		/* Parse SIG models */
+		while (len >= 2 && m--) {
+			mod_id = l_get_le16(data);
+			mod = mesh_model_new(ele->idx, mod_id);
+			if (!mod) {
+				element_free(ele);
+				goto fail;
+			}
+
+			l_queue_push_tail(ele->models, mod);
+			data += 2;
+			len -= 2;
+		}
+
+		if (v && len < 4) {
+			element_free(ele);
+			goto fail;
+		}
+
+		/* Parse vendor models */
+		while (len >= 4 && v--) {
+			mod_id = l_get_le16(data + 2);
+			vendor_id = l_get_le16(data);
+			mod_id |= (vendor_id << 16);
+			mod = mesh_model_vendor_new(ele->idx, vendor_id,
+									mod_id);
+			if (!mod) {
+				element_free(ele);
+				goto fail;
+			}
+
+			l_queue_push_tail(ele->models, mod);
+			data += 4;
+			len -= 4;
+		}
+
+		num_ele++;
+		l_queue_push_tail(node->elements, ele);
+
+	} while (len >= 6);
+
+	/* Check the consistency for the remote node */
+	if (node->num_ele > num_ele)
+		goto fail;
+
+	node->comp = comp;
+	node->num_ele = num_ele;
+
+	return true;
+
+fail:
+	l_queue_destroy(node->elements, element_free);
+	l_free(comp);
+
+	return false;
+}
+
+void node_id_set(struct mesh_node *node, uint16_t id)
+{
+	if (node)
+		node->id = id;
+}
+
+static void attach_io(void *a, void *b)
+{
+	struct mesh_node *node = a;
+	struct mesh_io *io = b;
+
+	if (node->net)
+		mesh_net_attach(node->net, io);
+}
+
+/* Register callbacks for io */
+void node_attach_io(struct mesh_io *io)
+{
+	l_queue_foreach(nodes, attach_io, io);
+}
+
+static bool register_node_object(struct mesh_node *node)
+{
+	node->path = l_malloc(strlen(MESH_NODE_PATH_PREFIX) + 5);
+
+	snprintf(node->path, 10, MESH_NODE_PATH_PREFIX "%4.4x", node->id);
+
+	if (!l_dbus_object_add_interface(dbus_get_bus(), node->path,
+					MESH_NODE_INTERFACE, node))
+		return false;
+
+	return true;
+}
+
+static void app_disc_cb(struct l_dbus *bus, void *user_data)
+{
+	struct mesh_node *node = user_data;
+
+	l_info("App %s disconnected (%u)", node->owner, node->disc_watch);
+
+	node->disc_watch = 0;
+
+	l_queue_foreach(node->elements, free_element_path, NULL);
+
+	l_free(node->owner);
+	node->owner = NULL;
+
+	l_free(node->app_path);
+	node->app_path = NULL;
+}
+
+static bool validate_element_properties(struct mesh_node *node,
+					const char *path,
+					struct l_dbus_message_iter *properties)
+{
+	uint8_t ele_idx;
+	struct node_element *ele;
+	const char *key;
+	struct l_dbus_message_iter variant;
+	bool have_index = false;
+
+	l_debug("path %s", path);
+
+	while (l_dbus_message_iter_next_entry(properties, &key, &variant)) {
+		if (!strcmp(key, "Index")) {
+			have_index = true;
+			break;
+		}
+	}
+
+	if (!have_index) {
+		l_debug("Mandatory property \"Index\" not found");
+		return false;
+	}
+
+	if (!l_dbus_message_iter_get_variant(&variant, "y", &ele_idx))
+		return false;
+
+	ele = l_queue_find(node->elements, match_element_idx,
+							L_UINT_TO_PTR(ele_idx));
+
+	if (!ele) {
+		l_debug("Element with index %u not found", ele_idx);
+		return false;
+	}
+
+	/* TODO: validate models */
+
+	ele->path = l_strdup(path);
+
+	return true;
+}
+
+static void get_managed_objects_attach_cb(struct l_dbus_message *msg,
+								void *user_data)
+{
+	struct l_dbus_message_iter objects, interfaces;
+	struct attach_obj_request *req = user_data;
+	struct mesh_node *node = req->node;
+	const char *path;
+	uint64_t token = l_get_u64(node->dev_key);
+	uint8_t num_ele;
+
+	if (l_dbus_message_is_error(msg)) {
+		l_error("Failed to get app's dbus objects");
+		goto fail;
+	}
+
+	if (!l_dbus_message_get_arguments(msg, "a{oa{sa{sv}}}", &objects)) {
+		l_error("Failed to parse app's dbus objects");
+		goto fail;
+	}
+
+	num_ele = 0;
+
+	while (l_dbus_message_iter_next_entry(&objects, &path, &interfaces)) {
+		struct l_dbus_message_iter properties;
+		const char *interface;
+
+		while (l_dbus_message_iter_next_entry(&interfaces, &interface,
+								&properties)) {
+			if (strcmp(MESH_ELEMENT_INTERFACE, interface))
+				continue;
+
+			if (!validate_element_properties(node, path,
+								&properties))
+				goto fail;
+
+			num_ele++;
+		}
+	}
+
+	/*
+	 * Check that the number of element objects matches the expected number
+	 * of elements on the node
+	 */
+	if (num_ele != node->num_ele)
+		goto fail;
+
+	/* Register node object with D-Bus */
+	register_node_object(node);
+
+	if (node->path) {
+		struct l_dbus *bus = dbus_get_bus();
+
+		node->disc_watch = l_dbus_add_disconnect_watch(bus, node->owner,
+						app_disc_cb, node, NULL);
+		req->cb(MESH_ERROR_NONE, node->path, token);
+
+		return;
+	}
+fail:
+	req->cb(MESH_ERROR_FAILED, NULL, token);
+
+	l_queue_foreach(node->elements, free_element_path, NULL);
+	l_free(node->app_path);
+	node->app_path = NULL;
+
+	l_free(node->owner);
+	node->owner = NULL;
+}
+
+/* Establish relationship between application and mesh node */
+int node_attach(const char *app_path, const char *sender, uint64_t token,
+						node_attach_ready_func_t cb)
+{
+	struct attach_obj_request *req;
+	struct mesh_node *node;
+
+	l_debug("");
+
+	node = l_queue_find(nodes, match_token, &token);
+	if (!node)
+		return MESH_ERROR_NOT_FOUND;
+
+	/* TODO: decide what to do if previous node->app_path is not NULL */
+	node->app_path = l_strdup(app_path);
+
+	node->owner = l_strdup(sender);
+
+	req = l_new(struct attach_obj_request, 1);
+	req->node = node;
+	req->cb = cb;
+
+	l_dbus_method_call(dbus_get_bus(), sender, app_path,
+					L_DBUS_INTERFACE_OBJECT_MANAGER,
+					"GetManagedObjects", NULL,
+					get_managed_objects_attach_cb,
+					req, l_free);
+	return MESH_ERROR_NONE;
+
+}
+
+static void add_model_from_properties(struct node_element *ele,
+					struct l_dbus_message_iter *property)
+{
+	struct l_dbus_message_iter ids;
+	uint16_t model_id;
+	int i = 0;
+
+	if (!ele->models)
+		ele->models = l_queue_new();
+
+	if (!l_dbus_message_iter_get_variant(property, "aq", &ids))
+		return;
+
+	while (l_dbus_message_iter_next_entry(&ids, &model_id)) {
+		struct mesh_model *mod;
+		l_debug("model_id %4.4x", model_id);
+		mod = mesh_model_new(ele->idx, model_id);
+		l_queue_push_tail(ele->models, mod);
+		i++;
+		if (i > 3)
+			break;
+	}
+}
+
+static void add_vendor_model_from_properties(struct node_element *ele,
+					struct l_dbus_message_iter *property)
+{
+	struct {
+		uint16_t v;
+		uint16_t m;
+	} id_pair;
+
+	if (!ele->models)
+		ele->models = l_queue_new();
+
+	while (l_dbus_message_iter_next_entry(property, &id_pair)) {
+		struct mesh_model *mod;
+		mod = mesh_model_vendor_new(ele->idx, id_pair.v, id_pair.m);
+		l_queue_push_tail(ele->models, mod);
+	}
+}
+
+static bool get_element_properties(struct mesh_node *node, const char *path,
+					struct l_dbus_message_iter *properties)
+{
+	struct node_element *ele;
+	const char *key;
+	struct l_dbus_message_iter variant;
+	bool have_index = false;
+
+	l_debug("path %s", path);
+
+	ele = l_new(struct node_element, 1);
+	ele->location = DEFAULT_LOCATION;
+
+	while (l_dbus_message_iter_next_entry(properties, &key, &variant)) {
+		if (!strcmp(key, "Index")) {
+			if (!l_dbus_message_iter_get_variant(&variant, "y",
+								&ele->idx))
+				return false;
+			have_index = true;
+		} else if (!strcmp(key, "Location")) {
+			l_dbus_message_iter_get_variant(&variant, "q",
+								&ele->location);
+		} else if (!strcmp(key, "Models")) {
+			add_model_from_properties(ele, &variant);
+		} else if (!strcmp(key, "VendorModels")) {
+			add_vendor_model_from_properties(ele, &variant);
+		}
+	}
+
+	if (!have_index) {
+		l_debug("Mandatory property \"Index\" not found");
+		return false;
+	}
+
+	l_queue_push_tail(node->elements, ele);
+
+	return true;
+}
+
+static bool get_app_properties(struct mesh_node *node, const char *path,
+					struct l_dbus_message_iter *properties)
+{
+	const char *key;
+	struct l_dbus_message_iter variant;
+
+	l_debug("path %s", path);
+
+	if (!node->comp)
+		node->comp = l_new(struct node_composition, 1);
+
+	while (l_dbus_message_iter_next_entry(properties, &key, &variant)) {
+
+		if (!strcmp(key, "CompanyID")) {
+			if (!l_dbus_message_iter_get_variant(&variant, "q",
+							&node->comp->cid))
+				return false;
+		} else if (!strcmp(key, "ProductID")) {
+			if (!l_dbus_message_iter_get_variant(&variant, "q",
+							&node->comp->pid))
+				return false;
+		} else if (!strcmp(key, "VersionID")) {
+			if (!l_dbus_message_iter_get_variant(&variant, "q",
+							&node->comp->vid))
+				return false;
+		}
+	}
+
+	return true;
+}
+
+static void convert_node_to_storage(struct mesh_node *node,
+						struct mesh_db_node *db_node)
+{
+	const struct l_queue_entry *entry;
+
+	db_node->cid = node->comp->cid;
+	db_node->pid = node->comp->pid;
+	db_node->vid = node->comp->vid;
+	db_node->crpl = node->comp->crpl;
+	db_node->modes.lpn = node->lpn;
+	db_node->modes.proxy = node->proxy;
+
+	memcpy(db_node->uuid, node->dev_uuid, 16);
+
+	node->friend = db_node->modes.friend;
+	db_node->modes.relay.state = node->relay.mode;
+	db_node->modes.relay.cnt = node->relay.cnt;
+	db_node->modes.relay.interval = node->relay.interval;
+	db_node->modes.beacon = node->beacon;
+
+	db_node->ttl = node->ttl;
+	db_node->seq_number = node->seq_number;
+
+	db_node->elements = l_queue_new();
+
+	entry = l_queue_get_entries(node->elements);
+
+	for (; entry; entry = entry->next) {
+		struct node_element *ele = entry->data;
+		struct mesh_db_element *db_ele;
+		const struct l_queue_entry *mod_entry;
+
+		db_ele = l_new(struct mesh_db_element, 1);
+
+		db_ele->index = ele->idx;
+		db_ele->location = ele->location;
+		db_ele->models = l_queue_new();
+
+		mod_entry = l_queue_get_entries(ele->models);
+
+		for (; mod_entry; mod_entry = mod_entry->next) {
+			struct mesh_model *mod = mod_entry->data;
+			struct mesh_db_model *db_mod;
+			uint32_t mod_id = mesh_model_get_model_id(mod);
+
+			db_mod = l_new(struct mesh_db_model, 1);
+			db_mod->id = mod_id;
+			db_mod->vendor = ((mod_id & VENDOR_ID_MASK)
+							!= VENDOR_ID_MASK);
+
+			l_queue_push_tail(db_ele->models, db_mod);
+		}
+		l_queue_push_tail(db_node->elements, db_ele);
+	}
+
+}
+
+static bool create_node_config(struct mesh_node *node)
+{
+	struct mesh_db_node db_node;
+	const struct l_queue_entry *entry;
+	bool res;
+
+	convert_node_to_storage(node, &db_node);
+	res = storage_create_node_config(node, &db_node);
+
+	/* Free temporarily allocated resources */
+	entry = l_queue_get_entries(db_node.elements);
+	for (; entry; entry = entry->next) {
+		struct mesh_db_element *db_ele = entry->data;
+
+		l_queue_destroy(db_ele->models, l_free);
+	}
+
+	l_queue_destroy(db_node.elements, l_free);
+
+	return res;
+}
+
+static void set_defaults(struct mesh_node *node)
+{
+	/* TODO: these values should come from mesh.conf */
+	if (!node->comp)
+		node->comp = l_new(struct node_composition, 1);
+
+	node->comp->crpl = DEFAULT_CRPL;
+	node->lpn = MESH_MODE_UNSUPPORTED;
+	node->proxy = MESH_MODE_UNSUPPORTED;
+	node->friend = MESH_MODE_UNSUPPORTED;
+	node->beacon = MESH_MODE_DISABLED;
+	node->relay.mode = MESH_MODE_DISABLED;
+	node->ttl = DEFAULT_TTL;
+	node->seq_number = DEFAULT_SEQUENCE_NUMBER;
+
+	/* Add configuration server model on primary element */
+	add_internal_model(node, CONFIG_SRV_MODEL, PRIMARY_ELE_IDX);
+}
+
+static void get_managed_objects_join_cb(struct l_dbus_message *msg,
+								void *user_data)
+{
+	struct l_dbus_message_iter objects, interfaces;
+	struct join_obj_request *req = user_data;
+	const char *path;
+	struct mesh_node *node = NULL;
+	void *agent = NULL;
+
+	if (l_dbus_message_is_error(msg)) {
+		l_error("Failed to get app's dbus objects");
+		goto fail;
+	}
+
+	if (!l_dbus_message_get_arguments(msg, "a{oa{sa{sv}}}", &objects)) {
+		l_error("Failed to parse app's dbus objects");
+		goto fail;
+	}
+
+	node = l_new(struct mesh_node, 1);
+	node->elements = l_queue_new();
+
+	while (l_dbus_message_iter_next_entry(&objects, &path, &interfaces)) {
+		struct l_dbus_message_iter properties;
+		const char *interface;
+
+		while (l_dbus_message_iter_next_entry(&interfaces, &interface,
+								&properties)) {
+			bool res;
+
+			if (!strcmp(MESH_ELEMENT_INTERFACE, interface)) {
+				res = get_element_properties(node, path,
+								&properties);
+				if (!res)
+					goto fail;
+
+				node->num_ele++;
+				continue;
+
+			}
+
+			if (!strcmp(MESH_APPLICATION_INTERFACE, interface)) {
+				res = get_app_properties(node, path,
+								&properties);
+				if (!res)
+					goto fail;
+
+				continue;
+			}
+
+			if (!strcmp(MESH_PROVISION_AGENT_INTERFACE,
+								interface)) {
+				const char *sender;
+
+				sender = l_dbus_message_get_sender(msg);
+				agent = mesh_agent_create(path, sender,
+								&properties);
+				if (!agent)
+					goto fail;
+			}
+		}
+	}
+
+	if (!node->comp){
+		l_error("Interface %s not found", MESH_APPLICATION_INTERFACE);
+		goto fail;
+	}
+
+	if (!agent) {
+		l_error("Interface %s not found",
+						MESH_PROVISION_AGENT_INTERFACE);
+		goto fail;
+	}
+
+	if (!node->num_ele) {
+		l_error("Interface %s not found", MESH_ELEMENT_INTERFACE);
+		goto fail;
+	}
+
+	if (!l_queue_find(node->elements, match_element_idx,
+				L_UINT_TO_PTR(PRIMARY_ELE_IDX))) {
+
+		l_debug("Primary element not detected");
+		goto fail;
+	}
+
+	set_defaults(node);
+	memcpy(node->dev_uuid, req->uuid, 16);
+
+	if (!create_node_config(node))
+		goto fail;
+
+	req->cb(node, agent);
+
+	return;
+fail:
+	if (agent)
+		free_node_resources(node);
+
+	if (node)
+		mesh_agent_remove(agent);
+
+	req->cb(NULL, NULL);
+}
+
+/* Create a temporary pre-provisioned node */
+void node_join(const char *app_path, const char *sender, const uint8_t *uuid,
+						node_join_ready_func_t cb)
+{
+	struct join_obj_request *req;
+
+	l_debug("");
+
+	req = l_new(struct join_obj_request, 1);
+	req->uuid = uuid;
+	req->cb = cb;
+
+	l_dbus_method_call(dbus_get_bus(), sender, app_path,
+					L_DBUS_INTERFACE_OBJECT_MANAGER,
+					"GetManagedObjects", NULL,
+					get_managed_objects_join_cb,
+					req, l_free);
+}
+
+static void build_element_config(void *a, void *b)
+{
+	struct node_element *ele = a;
+	struct l_dbus_message_builder *builder = b;
+
+	l_debug("Element %u", ele->idx);
+
+	l_dbus_message_builder_enter_struct(builder, "ya(qa{sv})");
+
+	/* Element index */
+	l_dbus_message_builder_append_basic(builder, 'y', &ele->idx);
+
+	l_dbus_message_builder_enter_array(builder, "(qa{sv})");
+
+	/* Iterate over models */
+	l_queue_foreach(ele->models, model_build_config, builder);
+
+	l_dbus_message_builder_leave_array(builder);
+
+	l_dbus_message_builder_leave_struct(builder);
+}
+
+void node_build_attach_reply(struct l_dbus_message *reply, uint64_t token)
+{
+	struct mesh_node *node;
+	struct l_dbus_message_builder *builder;
+
+	node = l_queue_find(nodes, match_token, &token);
+	if (!node)
+		return;
+
+	builder = l_dbus_message_builder_new(reply);
+
+	/* Node object path */
+	l_dbus_message_builder_append_basic(builder, 'o', node->path);
+
+	/* Array of element configurations "a*/
+	l_dbus_message_builder_enter_array(builder, "(ya(qa{sv}))");
+	l_queue_foreach(node->elements, build_element_config, builder);
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+}
+
+static struct l_dbus_message *send_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct mesh_node *node = user_data;
+	const char *sender, *ele_path;
+	struct l_dbus_message_iter iter_data;
+	struct node_element *ele;
+	uint16_t dst, app_idx, src;
+	uint8_t data[MESH_MAX_ACCESS_PAYLOAD];
+	uint32_t len;
+	struct l_dbus_message *reply;
+
+	l_debug("Send");
+
+	sender = l_dbus_message_get_sender(msg);
+
+	if (strcmp(sender, node->owner))
+		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+	if (!l_dbus_message_get_arguments(msg, "oqqay", &ele_path, &dst,
+							&app_idx, &iter_data))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	ele = l_queue_find(node->elements, match_element_path, ele_path);
+	if (!ele)
+		return dbus_error(msg, MESH_ERROR_NOT_FOUND,
+							"Element not found");
+
+	src = node_get_primary(node) + ele->idx;
+
+	l_dbus_message_iter_get_fixed_array(&iter_data, data, &len);
+	if (!len)
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+						"Mesh message is empty");
+
+	if (!mesh_model_send(node, src, dst, app_idx,
+				mesh_net_get_default_ttl(node->net), data, len))
+		return dbus_error(msg, MESH_ERROR_FAILED, NULL);
+
+	reply = l_dbus_message_new_method_return(msg);
+	l_dbus_message_set_arguments(reply, "");
+
+	return reply;
+}
+
+static struct l_dbus_message *publish_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct mesh_node *node = user_data;
+	const char *sender, *ele_path;
+	struct l_dbus_message_iter iter_data;
+	uint16_t mod_id, src;
+	struct node_element *ele;
+	uint8_t data[MESH_MAX_ACCESS_PAYLOAD];
+	uint32_t len;
+	struct l_dbus_message *reply;
+	int result;
+
+	l_debug("Publish");
+
+	sender = l_dbus_message_get_sender(msg);
+
+	if (strcmp(sender, node->owner))
+		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+	if (!l_dbus_message_get_arguments(msg, "oqay", &ele_path, &mod_id,
+								&iter_data))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	ele = l_queue_find(node->elements, match_element_path, ele_path);
+	if (!ele)
+		return dbus_error(msg, MESH_ERROR_NOT_FOUND,
+							"Element not found");
+
+	src = node_get_primary(node) + ele->idx;
+
+	l_dbus_message_iter_get_fixed_array(&iter_data, data, &len);
+	if (!len)
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+						"Mesh message is empty");
+
+	result = mesh_model_publish(node, VENDOR_ID_MASK | mod_id, src,
+				mesh_net_get_default_ttl(node->net), data, len);
+
+	if (result != MESH_ERROR_NONE)
+		return dbus_error(msg, result, NULL);
+
+	reply = l_dbus_message_new_method_return(msg);
+	l_dbus_message_set_arguments(reply, "");
+
+	return reply;
+}
+
+static struct l_dbus_message *vendor_publish_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct mesh_node *node = user_data;
+	const char *sender, *ele_path;
+	struct l_dbus_message_iter iter_data;
+	uint16_t src;
+	uint16_t model_id, vendor;
+	uint32_t vendor_mod_id;
+	struct node_element *ele;
+	uint8_t data[MESH_MAX_ACCESS_PAYLOAD];
+	uint32_t len;
+	struct l_dbus_message *reply;
+	int result;
+
+	l_debug("Publish");
+
+	sender = l_dbus_message_get_sender(msg);
+
+	if (strcmp(sender, node->owner))
+		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+	if (!l_dbus_message_get_arguments(msg, "oqqay", &ele_path, &vendor,
+							&model_id, &iter_data))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	ele = l_queue_find(node->elements, match_element_path, ele_path);
+	if (!ele)
+		return dbus_error(msg, MESH_ERROR_NOT_FOUND,
+							"Element not found");
+
+	src = node_get_primary(node) + ele->idx;
+
+	l_dbus_message_iter_get_fixed_array(&iter_data, data, &len);
+	if (!len)
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+						"Mesh message is empty");
+
+	vendor_mod_id = (vendor << 16) | model_id;
+	result = mesh_model_publish(node, vendor_mod_id, src,
+				mesh_net_get_default_ttl(node->net), data, len);
+
+	if (result != MESH_ERROR_NONE)
+		return dbus_error(msg, result, NULL);
+
+	reply = l_dbus_message_new_method_return(msg);
+	l_dbus_message_set_arguments(reply, "");
+
+	return reply;
+}
+
+static void setup_node_interface(struct l_dbus_interface *iface)
+{
+	l_dbus_interface_method(iface, "Send", 0, send_call, "", "oqqay",
+						"element_path", "destination",
+						"key", "data");
+	l_dbus_interface_method(iface, "Publish", 0, publish_call, "", "oqay",
+					"element_path", "model_id", "data");
+	l_dbus_interface_method(iface, "VendorPublish", 0, vendor_publish_call,
+						"", "oqqay", "element_path",
+						"vendor", "model_id", "data");
+
+	/*TODO: Properties */
+}
+
+bool node_dbus_init(struct l_dbus *bus)
+{
+	if (!l_dbus_register_interface(bus, MESH_NODE_INTERFACE,
+						setup_node_interface,
+						NULL, false)) {
+		l_info("Unable to register %s interface", MESH_NODE_INTERFACE);
+		return false;
+	}
+
+	return true;
+}
+
+const char *node_get_owner(struct mesh_node *node)
+{
+	return node->owner;
+}
+
+const char *node_get_element_path(struct mesh_node *node, uint8_t ele_idx)
+{
+	struct node_element *ele;
+
+	ele = l_queue_find(node->elements, match_element_idx,
+							L_UINT_TO_PTR(ele_idx));
+
+	if (!ele)
+		return NULL;
+
+	return ele->path;
+}
+
+bool node_add_pending_local(struct mesh_node *node, void *prov_node_info,
+							struct mesh_io *io)
+{
+	struct mesh_prov_node_info *info = prov_node_info;
+	bool kr = !!(info->flags & PROV_FLAG_KR);
+	bool ivu = !!(info->flags & PROV_FLAG_IVU);
+
+	node->net = mesh_net_new(node);
+
+	if (!nodes)
+		nodes = l_queue_new();
+
+	l_queue_push_tail(nodes, node);
+
+	if (!storage_set_iv_index(node->net, info->iv_index, ivu))
+		return false;
+
+	mesh_net_set_iv_index(node->net, info->iv_index, ivu);
+
+	if (!mesh_db_write_uint16_hex(node->jconfig, "unicastAddress",
+								info->unicast))
+		return false;
+
+	node->primary = info->unicast;
+	mesh_net_register_unicast(node->net, info->unicast, node->num_ele);
+
+	memcpy(node->dev_key, info->device_key, 16);
+	if (!mesh_db_write_device_key(node->jconfig, info->device_key))
+		return false;
+
+	if (mesh_net_add_key(node->net, kr, info->net_index,
+			info->net_key) != MESH_STATUS_SUCCESS)
+		return false;
+
+	if (!storage_net_key_add(node->net, info->net_index, info->net_key,
+			kr ? KEY_REFRESH_PHASE_TWO : KEY_REFRESH_PHASE_NONE))
+		return false;
+
+	if (!storage_save_config(node, true, NULL, NULL))
+		return false;
+
+	/* Initialize configuration server model */
+	mesh_config_srv_init(node, PRIMARY_ELE_IDX);
+
+	mesh_net_attach(node->net, io);
+
+	return true;
+}
+
+void node_jconfig_set(struct mesh_node *node, void *jconfig)
+{
+	node->jconfig = jconfig;
+}
+
+void *node_jconfig_get(struct mesh_node *node)
+{
+	return  node->jconfig;
+}
+
+void node_cfg_file_set(struct mesh_node *node, char *cfg)
+{
+	node->cfg_file = cfg;
+}
+
+char *node_cfg_file_get(struct mesh_node *node)
+{
+	return node->cfg_file;
+}
+
+struct mesh_net *node_get_net(struct mesh_node *node)
+{
+	return node->net;
+}
diff --git a/mesh/node.h b/mesh/node.h
index f417fe503..ab09b14b0 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -15,38 +15,42 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
-#include "mesh/mesh-db.h"
-
 struct mesh_net;
 struct mesh_node;
+struct mesh_io;
+struct mesh_agent;
 
 /* 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)
 
+typedef void (*node_attach_ready_func_t) (int status, char *node_path,
+								uint64_t token);
+
+typedef void (*node_join_ready_func_t) (struct mesh_node *node,
+						struct mesh_agent *agent);
+
 struct mesh_node *node_new(void);
 void node_free(struct mesh_node *node);
+void node_join(const char *app_path, const char *sender, const uint8_t *uuid,
+						node_join_ready_func_t cb);
 uint8_t *node_uuid_get(struct mesh_node *node);
+struct mesh_net *node_get_net(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]);
+void 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,
@@ -58,12 +62,8 @@ 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);
+bool node_init_from_storage(struct mesh_node *node, void *data);
 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,
@@ -77,4 +77,19 @@ 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);
+const char *node_get_element_path(struct mesh_node *node, uint8_t ele_idx);
+const char *node_get_owner(struct mesh_node *node);
+bool node_add_pending_local(struct mesh_node *node, void *info,
+							struct mesh_io *io);
+void node_attach_io(struct mesh_io *io);
+int node_attach(const char *app_path, const char *sender, uint64_t token,
+						node_attach_ready_func_t cb);
+void node_build_attach_reply(struct l_dbus_message *reply, uint64_t token);
+void node_id_set(struct mesh_node *node, uint16_t node_id);
+bool node_dbus_init(struct l_dbus *bus);
+void node_cleanup(void *node);
+void node_cleanup_all(void);
+void node_jconfig_set(struct mesh_node *node, void *jconfig);
+void *node_jconfig_get(struct mesh_node *node);
+void node_cfg_file_set(struct mesh_node *node, char *cfg);
+char *node_cfg_file_get(struct mesh_node *node);
-- 
2.14.5


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

* [PATCH BlueZ v6 06/26] mesh: Rewrite Network layer for multiple nodes
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (4 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 05/26] mesh: Rewrite Node handling for multiple nodes Brian Gix
@ 2018-12-28 22:07 ` " Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 07/26] mesh: Direction agnostic PB-ADV implementation Brian Gix
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

The Network layer of the Mesh Daemon has access to all
network keys that have been entrusted to Nodes on the local
device, and acts as a MUX by forwarding incoming messages
to some, all or none of the local Nodes, based on addressing
and keys.

It also is reponsible for for applying Network layer encryption
to outgoing messages, and for relaying messages based on TTL.
---
 mesh/net.c | 294 ++++++++++++++++---------------------------------------------
 mesh/net.h |  34 ++-----
 2 files changed, 84 insertions(+), 244 deletions(-)

diff --git a/mesh/net.c b/mesh/net.c
index 97b6f5b16..842d899d7 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -26,11 +26,11 @@
 #include <stdio.h>
 #include <sys/time.h>
 #include <ell/ell.h>
+#include <json-c/json.h>
 
 #include "mesh/mesh-defs.h"
 #include "mesh/util.h"
 
-#include "mesh/display.h"
 #include "mesh/crypto.h"
 #include "mesh/net_keys.h"
 #include "mesh/mesh.h"
@@ -112,12 +112,9 @@ struct mesh_subnet {
 };
 
 struct mesh_net {
-	int ref_count;
 	struct mesh_io *io;
-	struct mesh_node *local_node;
+	struct mesh_node *node;
 	struct mesh_prov *prov;
-	void *jconfig_local;
-	const char *cfg_file;
 	struct l_queue *app_keys;
 	unsigned int pkt_id;
 	unsigned int bea_id;
@@ -129,7 +126,6 @@ struct mesh_net {
 	bool beacon_enable;
 	bool proxy_enable;
 	bool provisioner;
-	bool provisioned;
 	bool friend_seq;
 	struct l_timeout *iv_update_timeout;
 	enum _iv_upd_state iv_upd_state;
@@ -139,7 +135,6 @@ struct mesh_net {
 	uint32_t iv_index;
 	uint32_t seq_num;
 	uint32_t cached_seq_num;
-	uint16_t crpl;
 	uint16_t src_addr;
 	uint16_t last_addr;
 	uint16_t friend_addr;
@@ -253,6 +248,8 @@ struct net_decode {
 	bool proxy;
 };
 
+static struct l_queue *nets;
+
 static inline struct mesh_subnet *get_primary_subnet(struct mesh_net *net)
 {
 	return l_queue_peek_head(net->subnets);
@@ -499,8 +496,8 @@ uint32_t mesh_net_next_seq_num(struct mesh_net *net)
 	/* Periodically store advanced sequence number */
 	if (net->seq_num + MIN_SEQ_TRIGGER >= net->cached_seq_num) {
 		net->cached_seq_num = net->seq_num +
-					node_seq_cache(net->local_node);
-		node_set_sequence_number(net->local_node, net->cached_seq_num);
+					node_seq_cache(net->node);
+		node_set_sequence_number(net->node, net->cached_seq_num);
 	}
 
 	return seq;
@@ -647,15 +644,13 @@ static void start_network_beacon(void *a, void *b)
 				network_beacon_timeout, subnet, NULL);
 }
 
-struct mesh_net *mesh_net_new(uint16_t index)
+struct mesh_net *mesh_net_new(struct mesh_node *node)
 {
 	struct mesh_net *net;
 
 	net = l_new(struct mesh_net, 1);
 
-	if (!net)
-		return NULL;
-
+	net->node = node;
 	net->pkt_id = 0;
 	net->bea_id = 0;
 	net->key_id_next = 0;
@@ -689,27 +684,17 @@ struct mesh_net *mesh_net_new(uint16_t index)
 
 	memset(&net->heartbeat, 0, sizeof(net->heartbeat));
 
-	return mesh_net_ref(net);
-}
-
-struct mesh_net *mesh_net_ref(struct mesh_net *net)
-{
-	if (!net)
-		return NULL;
-
-	__sync_fetch_and_add(&net->ref_count, 1);
+	if (!nets)
+		nets = l_queue_new();
 
 	return net;
 }
 
-void mesh_net_unref(struct mesh_net *net)
+void mesh_net_free(struct mesh_net *net)
 {
 	if (!net)
 		return;
 
-	if (__sync_sub_and_fetch(&net->ref_count, 1))
-		return;
-
 	l_queue_destroy(net->subnets, subnet_free);
 	l_queue_destroy(net->fast_cache, mesh_msg_free);
 	l_queue_destroy(net->msg_cache, mesh_msg_free);
@@ -773,7 +758,6 @@ bool mesh_net_register_unicast(struct mesh_net *net,
 	if (!net || !IS_UNICAST(address) || !num_ele)
 		return false;
 
-	l_info("mesh_net_set_address: 0x%x", address);
 	net->src_addr = address;
 	net->last_addr = address + num_ele - 1;
 	if (net->last_addr < net->src_addr)
@@ -971,7 +955,7 @@ int mesh_net_del_key(struct mesh_net *net, uint16_t idx)
 	l_queue_remove(net->subnets, subnet);
 	subnet_free(subnet);
 
-	if (!storage_local_net_key_del(net, idx))
+	if (!storage_net_key_del(net, idx))
 		return MESH_STATUS_STORAGE_FAIL;
 
 	return MESH_STATUS_SUCCESS;
@@ -991,7 +975,7 @@ int mesh_net_add_key(struct mesh_net *net, bool update, uint16_t idx,
 			l_info("Start key refresh");
 			status = mesh_net_kr_phase_one(net, idx, value);
 			if (status == MESH_STATUS_SUCCESS &&
-				!storage_local_net_key_add(net, idx,
+				!storage_net_key_add(net, idx,
 						value, KEY_REFRESH_PHASE_ONE))
 				return MESH_STATUS_STORAGE_FAIL;
 		} else
@@ -1021,7 +1005,7 @@ int mesh_net_add_key(struct mesh_net *net, bool update, uint16_t idx,
 		return MESH_STATUS_INSUFF_RESOURCES;
 	}
 
-	if (!storage_local_net_key_add(net, idx, value,
+	if (!storage_net_key_add(net, idx, value,
 					KEY_REFRESH_PHASE_NONE)) {
 		l_queue_remove(net->subnets, subnet);
 		subnet_free(subnet);
@@ -1204,14 +1188,6 @@ static bool match_msg_timeout(const void *a, const void *b)
 	return sar->msg_timeout == msg_timeout;
 }
 
-static bool match_sar_id(const void *a, const void *b)
-{
-	const struct mesh_sar *sar = a;
-	unsigned int id = L_PTR_TO_UINT(b);
-
-	return sar->id == id;
-}
-
 static bool match_seg_timeout(const void *a, const void *b)
 {
 	const struct mesh_sar *sar = a;
@@ -1813,7 +1789,7 @@ static bool msg_rxed(struct mesh_net *net, bool frnd,
 	}
 
 not_for_friend:
-	return mesh_model_rx(net, szmic, seqAuth, seq, iv_index,
+	return mesh_model_rx(net->node, szmic, seqAuth, seq, iv_index,
 					ttl, src, dst, key_id, data, size);
 }
 
@@ -2511,22 +2487,40 @@ static void packet_received(void *user_data, const void *data, uint8_t size,
 		send_relay_pkt(net, packet, size + 1);
 }
 
+struct net_queue_data {
+	struct mesh_io_recv_info *info;
+	const uint8_t *data;
+	uint16_t len;
+};
+
+static void net_rx(void *net_ptr, void *user_data)
+{
+	struct net_queue_data *data = user_data;
+	struct mesh_net *net = net_ptr;
+	int8_t rssi = 0;
+
+	if (data->info) {
+		net->instant = data->info->instant;
+		net->chan = data->info->chan;
+		rssi = data->info->rssi;
+	}
+
+	packet_received(net, data->data, data->len, rssi);
+}
+
 static void net_msg_recv(void *user_data, struct mesh_io_recv_info *info,
 					const uint8_t *data, uint16_t len)
 {
-	struct mesh_net *net = user_data;
-	int8_t rssi = 0;
+	struct net_queue_data net_data = {
+		.info = info,
+		.data = data + 1,
+		.len = len - 1,
+	};
 
-	if (len <= 2 || !net)
+	if (len <= 2)
 		return;
 
-	if (info) {
-		net->instant = info->instant;
-		net->chan = info->chan;
-		rssi = info->rssi;
-	}
-
-	packet_received(user_data, data + 1, len - 1, rssi);
+	l_queue_foreach(nets, net_rx, &net_data);
 }
 
 static void set_network_beacon(void *a, void *b)
@@ -2653,7 +2647,7 @@ static void update_iv_kr_state(struct mesh_subnet *subnet, uint32_t iv_index,
 			net->iv_upd_state = IV_UPD_NORMAL;
 		}
 
-		storage_local_set_iv_index(net, iv_index, net->iv_upd_state);
+		storage_set_iv_index(net, iv_index, net->iv_upd_state);
 
 		/* Figure out the key refresh phase */
 		if (kr_transition) {
@@ -2675,7 +2669,7 @@ static void update_iv_kr_state(struct mesh_subnet *subnet, uint32_t iv_index,
 		net->iv_upd_state = IV_UPD_UPDATING;
 		net->iv_update_timeout = l_timeout_create(IV_IDX_UPD_MIN,
 							iv_upd_to, net, NULL);
-		storage_local_set_iv_index(net, iv_index, net->iv_upd_state);
+		storage_set_iv_index(net, iv_index, net->iv_upd_state);
 	} else if (iv_update && iv_index != net->iv_index) {
 		l_error("Update attempted too soon (iv idx already updated)");
 		return;
@@ -2688,7 +2682,7 @@ static void update_iv_kr_state(struct mesh_subnet *subnet, uint32_t iv_index,
 	if (iv_index > net->iv_index) {
 		l_queue_clear(net->msg_cache, mesh_msg_free);
 		net->iv_index = iv_index;
-		storage_local_set_iv_index(net, iv_index, net->iv_upd_state);
+		storage_set_iv_index(net, iv_index, net->iv_upd_state);
 	}
 
 	/* Figure out the key refresh phase */
@@ -2915,34 +2909,37 @@ bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable)
 	return true;
 }
 
+static bool is_this_net(const void *a, const void *b)
+{
+	return a == b;
+}
+
 bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)
 {
+	bool first;
+
 	if (!net)
 		return false;
 
-	net->io = io;
-	if (net->provisioned) {
+	first = l_queue_isempty(nets);
+	if (first) {
+		if (!nets)
+			nets = l_queue_new();
 
+		l_info("Register io cb");
 		mesh_io_register_recv_cb(io, MESH_IO_FILTER_BEACON,
 							beacon_recv, net);
 		mesh_io_register_recv_cb(io, MESH_IO_FILTER_NET,
-							net_msg_recv, net);
+							net_msg_recv, NULL);
 		l_queue_foreach(net->subnets, start_network_beacon, net);
+	}
 
-	} else {
-		uint8_t *uuid = node_uuid_get(net->local_node);
-
-		if (!uuid)
-			return false;
+	if (l_queue_find(nets, is_this_net, net))
+		return false;
 
-		mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
-		mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_NET);
+	l_queue_push_head(nets, net);
 
-		mesh_prov_listen(net, uuid, (uint8_t *) &net->prov_caps,
-					acceptor_prov_open,
-					acceptor_prov_close,
-					acceptor_prov_receive, net);
-	}
+	net->io = io;
 
 	return true;
 }
@@ -2952,10 +2949,10 @@ struct mesh_io *mesh_net_detach(struct mesh_net *net)
 	struct mesh_io *io;
 	uint8_t type = 0;
 
-	if (!net)
+	if (!net || !net->io)
 		return NULL;
 
-	io  = net->io;
+	io = net->io;
 
 	mesh_io_send_cancel(net->io, &type, 1);
 	mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
@@ -2975,7 +2972,7 @@ bool mesh_net_iv_index_update(struct mesh_net *net)
 	mesh_net_flush_msg_queues(net);
 	net->iv_upd_state = IV_UPD_UPDATING;
 	net->iv_index++;
-	if (!storage_local_set_iv_index(net, net->iv_index, IV_UPD_UPDATING))
+	if (!storage_set_iv_index(net, net->iv_index, IV_UPD_UPDATING))
 		return false;
 
 	l_queue_foreach(net->subnets, set_network_beacon, net);
@@ -3241,28 +3238,25 @@ void mesh_net_send_seg(struct mesh_net *net, uint32_t net_key_id,
 	l_free(str);
 }
 
-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,
+bool 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)
 {
 	struct mesh_sar *payload = NULL;
 	uint8_t seg, seg_max;
-	unsigned int ret = 0;
 	bool result;
 
 	if (!net || msg_len > 384)
-		return 0;
+		return false;
 
 	if (!src)
 		src = net->src_addr;
 
 	if (!src || !dst)
-		return 0;
+		return false;
 
 	if (ttl == 0xff)
 		ttl = net->default_ttl;
@@ -3287,7 +3281,7 @@ unsigned int mesh_net_app_send(struct mesh_net *net, bool frnd_cred,
 		/* Adjust our seq_num for "virtual" delivery */
 		net->seq_num += seg_max;
 		mesh_net_next_seq_num(net);
-		return 0;
+		return true;
 	}
 
 	/* If Segmented, Cancel any OB segmented message to same DST */
@@ -3337,29 +3331,13 @@ unsigned int mesh_net_app_send(struct mesh_net *net, bool frnd_cred,
 			l_timeout_create(MSG_TO, outmsg_to, net, NULL);
 		payload->status_func = status_func;
 		payload->user_data = user_data;
-		ret = payload->id = ++net->sar_id_next;
+		payload->id = ++net->sar_id_next;
 	} else
 		mesh_sar_free(payload);
 
-	return ret;
-}
-
-void mesh_net_app_send_cancel(struct mesh_net *net, unsigned int id)
-{
-	struct mesh_sar *sar = l_queue_remove_if(net->sar_out, match_sar_id,
-						L_UINT_TO_PTR(id));
-
-	if (sar) {
-		l_info("Canceling OB %d", id);
-		if (sar->status_func)
-			sar->status_func(sar->remote, 2,
-				sar->buf, sar->len - 4,
-				sar->user_data);
-	}
-	mesh_sar_free(sar);
+	return result;
 }
 
-/* TODO: add net key index */
 void mesh_net_ack_send(struct mesh_net *net, uint32_t key_id,
 				uint32_t iv_index,
 				uint8_t ttl,
@@ -3768,40 +3746,9 @@ uint32_t mesh_net_friend_timeout(struct mesh_net *net, uint16_t addr)
 		return frnd->poll_timeout;
 }
 
-bool mesh_net_local_node_set(struct mesh_net *net, struct mesh_node *node,
-							bool provisioner)
+struct mesh_node *mesh_net_node_get(struct mesh_net *net)
 {
-	if (net->local_node) {
-		l_info("Local node already registered");
-		return false;
-	}
-
-	net->local_node = node;
-	net->provisioner = provisioner;
-
-	return true;
-}
-
-struct mesh_node *mesh_net_local_node_get(struct mesh_net *net)
-{
-	return  net->local_node;
-}
-
-bool mesh_net_set_crpl(struct mesh_net *net, uint16_t crpl)
-{
-	if (!net)
-		return false;
-
-	net->crpl = crpl;
-	return true;
-}
-
-uint16_t mesh_net_get_crpl(struct mesh_net *net)
-{
-	if (!net)
-		return 0;
-
-	return net->crpl;
+	return  net->node;
 }
 
 struct l_queue *mesh_net_get_app_keys(struct mesh_net *net)
@@ -3824,41 +3771,6 @@ bool mesh_net_have_key(struct mesh_net *net, uint16_t idx)
 						L_UINT_TO_PTR(idx)) != NULL);
 }
 
-bool mesh_net_jconfig_set(struct mesh_net *net, void *jconfig)
-{
-	if (!net)
-		return false;
-
-	net->jconfig_local = jconfig;
-	return true;
-}
-
-void *mesh_net_jconfig_get(struct mesh_net *net)
-{
-	if (!net)
-		return NULL;
-
-	return  net->jconfig_local;
-}
-
-bool mesh_net_cfg_file_set(struct mesh_net *net, const char *cfg)
-{
-	if (!net)
-		return false;
-
-	net->cfg_file = cfg;
-	return true;
-}
-
-bool mesh_net_cfg_file_get(struct mesh_net *net, const char **cfg)
-{
-	if (!net)
-		return false;
-
-	*cfg = net->cfg_file;
-	return true;
-}
-
 bool mesh_net_is_local_address(struct mesh_net *net, uint16_t addr)
 {
 	if (!net)
@@ -3919,55 +3831,3 @@ void mesh_net_set_prov(struct mesh_net *net, struct mesh_prov *prov)
 	net->prov = prov;
 }
 
-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)
-{
-	if (net->provisioned || !net->local_node)
-		return false;
-
-	if (!node_set_primary(net->local_node, unicast) ||
-				!(mesh_net_register_unicast(net, unicast,
-						mesh_net_get_num_ele(net))))
-		return false;
-
-	if (!node_set_device_key(net->local_node, device_key))
-		return false;
-
-	net->iv_index = iv_index;
-	net->iv_update = ((snb_flags & 0x02) != 0);
-	if (!storage_local_set_iv_index(net, iv_index, net->iv_update))
-		return false;
-
-	if (mesh_net_add_key(net, false, net_idx, net_key) !=
-							MESH_STATUS_SUCCESS)
-		return false;
-
-	if ((snb_flags & 0x01) &&
-			(mesh_net_add_key(net, true, net_idx, net_key) !=
-							MESH_STATUS_SUCCESS)) {
-		l_queue_clear(net->subnets, l_free);
-		return false;
-	}
-
-	return storage_save_new_config(net, net->cfg_file, cb, user_data);
-
-}
-
-void mesh_net_provisioned_set(struct mesh_net *net, bool provisioned)
-{
-	if (!net)
-		return;
-
-	net->provisioned = provisioned;
-}
-
-bool mesh_net_provisioned_get(struct mesh_net *net)
-{
-	if (!net)
-		return false;
-
-	return net->provisioned;
-}
diff --git a/mesh/net.h b/mesh/net.h
index b8eb0699d..0ef01b63e 100644
--- a/mesh/net.h
+++ b/mesh/net.h
@@ -17,9 +17,6 @@
  *
  */
 
-#include <stdbool.h>
-#include <stdint.h>
-
 #ifndef __packed
 #define __packed __attribute__((packed))
 #endif
@@ -259,9 +256,8 @@ 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);
+struct mesh_net *mesh_net_new(struct mesh_node *node);
+void mesh_net_free(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);
@@ -301,13 +297,12 @@ void mesh_net_transport_send(struct mesh_net *net, uint32_t key_id,
 				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,
+bool 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, uint32_t key_id,
 				uint32_t iv_index, uint8_t ttl, uint32_t seq,
 				uint16_t src, uint16_t dst, bool rly,
@@ -363,16 +358,8 @@ 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);
+struct mesh_node *mesh_net_node_get(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,
@@ -381,10 +368,3 @@ 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);
-- 
2.14.5


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

* [PATCH BlueZ v6 07/26] mesh: Direction agnostic PB-ADV implementation
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (5 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 06/26] mesh: Rewrite Network layer " Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 08/26] mesh: Acceptor side provisioning implementation Brian Gix
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

Originally found in prov.c, this file implements the PB-ADV
provisioning transport.  It may be used by either the
Acceptor or the Initiator side of the Provisioning procedure,
but only one session may be active at once.
---
 mesh/pb-adv.c | 444 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 mesh/pb-adv.h |  23 +++
 2 files changed, 467 insertions(+)
 create mode 100644 mesh/pb-adv.c
 create mode 100644 mesh/pb-adv.h

diff --git a/mesh/pb-adv.c b/mesh/pb-adv.c
new file mode 100644
index 000000000..57647e184
--- /dev/null
+++ b/mesh/pb-adv.c
@@ -0,0 +1,444 @@
+/*
+ *
+ *  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 "mesh/mesh-defs.h"
+#include "src/shared/ecc.h"
+
+#include "mesh/util.h"
+#include "mesh/net_keys.h"
+#include "mesh/crypto.h"
+#include "mesh/net.h"
+#include "mesh/mesh-io.h"
+#include "mesh/mesh.h"
+#include "mesh/prov.h"
+#include "mesh/provision.h"
+#include "mesh/pb-adv.h"
+
+
+struct pb_adv_session {
+	mesh_prov_open_func_t open_cb;
+	mesh_prov_close_func_t close_cb;
+	mesh_prov_receive_func_t rx_cb;
+	mesh_prov_ack_func_t ack_cb;
+	struct l_timeout *tx_timeout;
+	uint32_t conn_id;
+	uint16_t exp_len;
+	uint8_t exp_fcs;
+	uint8_t exp_segs;
+	uint8_t got_segs;
+	uint8_t msg_num;
+	uint8_t local_acked;
+	uint8_t local_msg_num;
+	uint8_t peer_msg_num;
+	uint8_t last_peer_msg_num;
+	uint8_t sar[80];
+	uint8_t uuid[16];
+	bool initiator;
+	bool opened;
+	void *user_data;
+};
+
+#define PB_ADV_ACK 0x01
+#define PB_ADV_OPEN_REQ 0x03
+#define PB_ADV_OPEN_CFM 0x07
+#define PB_ADV_CLOSE 0x0B
+
+#define PB_ADV_MTU	24
+
+static struct pb_adv_session *pb_session = NULL;
+
+static void send_adv_segs(struct pb_adv_session *session, const uint8_t *data,
+							uint16_t size)
+{
+	uint16_t init_size;
+	uint8_t buf[PB_ADV_MTU + 6] = { MESH_AD_TYPE_PROVISION };
+	uint8_t max_seg;
+	uint8_t consumed;
+	int i;
+
+	if (!size)
+		return;
+
+	mesh_send_cancel(buf, 1);
+
+	l_put_be32(session->conn_id, buf + 1);
+	buf[1 + 4] = ++session->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[6] = max_seg << 2;
+	l_put_be16(size, buf + 7);
+	buf[9] = mesh_crypto_compute_fcs(data, size);
+	memcpy(buf + 10, 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_send_pkt(MESH_IO_TX_COUNT_UNLIMITED, 50, 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_send_pkt(MESH_IO_TX_COUNT_UNLIMITED, 50,
+							buf, seg_size + 7);
+
+		consumed += seg_size;
+	}
+}
+
+static void tx_timeout(struct l_timeout *timeout, void *user_data)
+{
+	struct pb_adv_session *session = user_data;
+	uint8_t cancel[] = { MESH_AD_TYPE_PROVISION };
+	mesh_prov_close_func_t cb;
+
+	if (!session || pb_session != session)
+		return;
+
+	l_timeout_remove(session->tx_timeout);
+	session->tx_timeout = NULL;
+
+	mesh_send_cancel(cancel, sizeof(cancel));
+
+	l_info("TX timeout");
+	cb = pb_session->close_cb;
+	user_data = pb_session->user_data;
+	l_free(pb_session);
+	pb_session = NULL;
+	cb(user_data, 1);
+}
+
+static void pb_adv_tx(void *user_data, uint8_t *data, uint16_t len)
+{
+	struct pb_adv_session *session = user_data;
+
+	if (!session || pb_session != session)
+		return;
+
+	l_timeout_remove(session->tx_timeout);
+	session->tx_timeout = l_timeout_create(30, tx_timeout, session, NULL);
+
+	send_adv_segs(session, data, len);
+}
+
+static void send_open_cfm(struct pb_adv_session *session)
+{
+	uint8_t open_cfm[7] = { MESH_AD_TYPE_PROVISION };
+
+	l_put_be32(session->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_send_cancel(open_cfm, 1);
+	mesh_send_pkt(5, 100, open_cfm, sizeof(open_cfm));
+}
+
+static void send_ack(struct pb_adv_session *session, uint8_t msg_num)
+{
+	uint8_t ack[7] = { MESH_AD_TYPE_PROVISION };
+
+	l_put_be32(session->conn_id, ack + 1);
+	ack[1 + 4] = msg_num;
+	ack[1 + 4 + 1] = 0x01; /* ACK */
+
+	/* print_packet("ADV-ACK", ack + 1, sizeof(ack) - 1); */
+	mesh_send_pkt(1, 100, ack, sizeof(ack));
+}
+
+static void send_close_ind(struct pb_adv_session *session, uint8_t reason)
+{
+	uint8_t close_ind[8] = { MESH_AD_TYPE_PROVISION };
+
+	if (!pb_session || pb_session->user_data != session)
+		return;
+
+	l_put_be32(session->conn_id, close_ind + 1);
+	close_ind[5] = 0;
+	close_ind[6] = PB_ADV_CLOSE;		/* CLOSE_IND */
+	close_ind[7] = reason;
+
+	mesh_send_cancel(close_ind, 1);
+	mesh_send_pkt(5, 100, close_ind, sizeof(close_ind));
+}
+
+static void pb_adv_packet(void *user_data, const uint8_t *pkt, uint16_t len)
+{
+	struct pb_adv_session *session = user_data;
+	uint32_t conn_id;
+	size_t offset;
+	uint8_t msg_num;
+	uint8_t type;
+	bool first;
+
+	if (!session || pb_session != session)
+		return;
+
+	conn_id = l_get_be32(pkt + 1);
+	type = l_get_u8(pkt + 6);
+
+	/* Validate new or existing Connection ID */
+	if (session->conn_id) {
+		if (session->conn_id != conn_id)
+			return;
+	} else if (type != 0x03)
+		return;
+	else if (!conn_id)
+		return;
+
+	msg_num = l_get_u8(pkt + 5);
+	pkt += 7;
+	len -= 7;
+
+	switch (type) {
+	case PB_ADV_OPEN_CFM:
+		/*
+		 * Ignore if:
+		 * 1. We are acceptor
+		 * 2. We are already provisioning on different conn_id
+		 */
+
+		if (!session->initiator)
+			return;
+
+		first = !session->opened;
+		session->opened = true;
+
+		/* Only call Open callback once */
+		if (first) {
+			l_debug("PB-ADV open confirmed");
+			session->open_cb(session->user_data, pb_adv_tx,
+							session, PB_ADV);
+		}
+		return;
+
+	case PB_ADV_OPEN_REQ:
+		/*
+		 * Ignore if:
+		 * 1. We are initiator
+		 * 2. Open request not addressed to us
+		 * 3. We are already provisioning on different conn_id
+		 */
+
+		if (session->initiator)
+			return;
+
+		if (memcmp(pkt, session->uuid, 16))
+			return;
+
+		first = !session->conn_id;
+		session->conn_id = conn_id;
+		session->last_peer_msg_num = 0xFF;
+		session->local_acked = 0xFF;
+		session->peer_msg_num = 0x00;
+		session->local_msg_num = 0x7F;
+		session->opened = true;
+
+		/* Only call Open callback once */
+		if (first) {
+			l_debug("PB-ADV open requested");
+			session->open_cb(session->user_data, pb_adv_tx,
+							session, PB_ADV);
+		}
+
+		/* Send CFM once per received request */
+		send_open_cfm(session);
+		break;
+
+	case PB_ADV_CLOSE:
+		l_timeout_remove(session->tx_timeout);
+		l_debug("Link closed notification: %2.2x", pkt[0]);
+		/* Wrap callback for pre-cleaning */
+		if (true) {
+			mesh_prov_close_func_t cb = session->close_cb;
+			void *user_data = session->user_data;
+
+			l_free(session);
+			pb_session = NULL;
+			cb(user_data, pkt[0]);
+		}
+		break;
+
+	case PB_ADV_ACK:
+		if (!session->opened)
+			return;
+
+		if (msg_num != session->local_msg_num)
+			return;
+
+		if (session->local_acked > msg_num)
+			return;
+
+		l_debug("Got ACK %d", msg_num);
+		session->local_acked = msg_num;
+		session->ack_cb(session->user_data, msg_num);
+		break;
+
+	default: /* DATA SEGMENT */
+		if (!session->opened)
+			return;
+
+		if (msg_num == session->last_peer_msg_num) {
+			send_ack(session, msg_num);
+			return;
+		}
+
+		switch(type & 0x03) {
+		case 0x00:
+			session->peer_msg_num = msg_num;
+			session->exp_len = l_get_be16(pkt);
+
+			l_debug("PB-ADV start with %u fragments, %d octets",
+						type >> 2, session->exp_len);
+
+			if (session->exp_len > sizeof(session->sar)) {
+				l_debug("Incoming length exceeded: %d",
+							session->exp_len);
+				return;
+			}
+
+			session->exp_fcs = l_get_u8(pkt + 2);
+			session->exp_segs = 0xff >> (7 - (type >> 2));
+
+			/* Save first segment */
+			memcpy(session->sar, pkt + 3, len - 3);
+			session->got_segs |= 1;
+			break;
+
+		case 0x02:
+			session->peer_msg_num = msg_num;
+			offset = 20 + (((type >> 2) - 1) * 23);
+
+			if (offset + len - 3 > sizeof(session->sar)) {
+				l_debug("Length exceeded: %d",
+							session->exp_len);
+				return;
+			}
+
+			l_debug("Processing fragment %u", type >> 2);
+			memcpy(session->sar + offset, pkt, len);
+			session->got_segs |= 1 << (type >> 2);
+			break;
+
+		default:
+			/* Malformed or unrecognized */
+			return;
+		}
+
+		if (session->got_segs != session->exp_segs)
+			return;
+
+		/* Validate RXed packet and pass up to Provisioning */
+		if (!mesh_crypto_check_fcs(session->sar,
+					session->exp_len,
+					session->exp_fcs)) {
+
+			/* This can be a false negative if first
+			 * segment missed, and can almost always
+			 * be ignored.
+			 */
+
+			l_debug("Invalid FCS");
+			return;
+		}
+
+		if (session->last_peer_msg_num != session->peer_msg_num) {
+			session->got_segs = 0;
+			session->rx_cb(session->user_data, session->sar,
+							session->exp_len);
+		}
+
+		session->last_peer_msg_num = session->peer_msg_num;
+		send_ack(session, session->last_peer_msg_num);
+	}
+}
+
+bool pb_adv_reg(mesh_prov_open_func_t open_cb, mesh_prov_close_func_t close_cb,
+		mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb,
+		uint8_t uuid[16], void *user_data)
+{
+	if (pb_session)
+		return false;
+
+	pb_session = l_new(struct pb_adv_session, 1);
+	pb_session->open_cb = open_cb;
+	pb_session->close_cb = close_cb;
+	pb_session->rx_cb = rx_cb;
+	pb_session->ack_cb = ack_cb;
+	pb_session->user_data = user_data;
+	memcpy(pb_session->uuid, uuid, 16);
+
+	/* TODO: register PB AD type and Start Beaconing ? */
+	mesh_reg_prov_rx(pb_adv_packet, pb_session);
+
+	return true;
+}
+
+void pb_adv_unreg(void *user_data)
+{
+	if (!pb_session || pb_session->user_data != user_data)
+		return;
+
+	l_timeout_remove(pb_session->tx_timeout);
+	send_close_ind(pb_session, 0);
+	l_free(pb_session);
+	pb_session = NULL;
+}
diff --git a/mesh/pb-adv.h b/mesh/pb-adv.h
new file mode 100644
index 000000000..a5870d5a2
--- /dev/null
+++ b/mesh/pb-adv.h
@@ -0,0 +1,23 @@
+/*
+ *
+ *  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.
+ *
+ */
+
+bool pb_adv_reg(mesh_prov_open_func_t open_cb, mesh_prov_close_func_t close_cb,
+		mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb,
+		uint8_t uuid[16], void *user_data);
+void pb_adv_unreg(void *user_data);
-- 
2.14.5


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

* [PATCH BlueZ v6 08/26] mesh: Acceptor side provisioning implementation
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (6 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 07/26] mesh: Direction agnostic PB-ADV implementation Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 09/26] mesh: Initiator " Brian Gix
                   ` (17 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

This functionaility (originally found in provision.c) was rewritten
as the Acceptor side only of the provisioning procedure. This
is the more common procedure of the unprovisioned device that is
brought into an existing mesh network by a remote (master) Provisioner.
---
 mesh/prov-acceptor.c | 684 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 684 insertions(+)
 create mode 100644 mesh/prov-acceptor.c

diff --git a/mesh/prov-acceptor.c b/mesh/prov-acceptor.c
new file mode 100644
index 000000000..baa3c4d30
--- /dev/null
+++ b/mesh/prov-acceptor.c
@@ -0,0 +1,684 @@
+/*
+ *
+ *  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 "mesh/mesh-defs.h"
+#include "src/shared/ecc.h"
+
+#include "mesh/util.h"
+#include "mesh/net_keys.h"
+#include "mesh/crypto.h"
+#include "mesh/net.h"
+#include "mesh/error.h"
+#include "mesh/prov.h"
+#include "mesh/provision.h"
+#include "mesh/pb-adv.h"
+#include "mesh/mesh.h"
+#include "mesh/agent.h"
+
+/* Quick size sanity check */
+static const uint16_t expected_pdu_size[] = {
+	2,	/* PROV_INVITE */
+	12,	/* PROV_CAPS */
+	6,	/* PROV_START */
+	65,	/* PROV_PUB_KEY */
+	1,	/* PROV_INP_CMPLT */
+	17,	/* PROV_CONFIRM */
+	17,	/* PROV_RANDOM */
+	34,	/* PROV_DATA */
+	1,	/* PROV_COMPLETE */
+	2,	/* PROV_FAILED */
+};
+
+#define BEACON_TYPE_UNPROVISIONED		0x00
+
+static const uint8_t pkt_filter = MESH_AD_TYPE_PROVISION;
+static const uint8_t bec_filter[] = {MESH_AD_TYPE_BEACON,
+						BEACON_TYPE_UNPROVISIONED};
+
+enum acp_state {
+	ACP_PROV_IDLE = 0,
+	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,
+};
+
+#define MAT_REMOTE_PUBLIC	0x01
+#define MAT_LOCAL_PRIVATE	0x02
+#define MAT_RAND_AUTH		0x04
+#define MAT_SECRET	(MAT_REMOTE_PUBLIC | MAT_LOCAL_PRIVATE)
+
+struct mesh_prov_acceptor {
+	mesh_prov_acceptor_complete_func_t cmplt;
+	prov_trans_tx_t trans_tx;
+	void *agent;
+	void *caller_data;
+	void *trans_data;
+	struct l_timeout *timeout;
+	uint32_t to_secs;
+	enum acp_state	state;
+	uint8_t transport;
+	uint8_t material;
+	uint8_t expected;
+	int8_t previous;
+	struct conf_input conf_inputs;
+	uint8_t calc_key[16];
+	uint8_t salt[16];
+	uint8_t confirm[16];
+	uint8_t s_key[16];
+	uint8_t s_nonce[13];
+	uint8_t private_key[32];
+	uint8_t secret[32];
+	uint8_t rand_auth_workspace[48];
+};
+
+static struct mesh_prov_acceptor *prov = NULL;
+
+static void acceptor_free(void)
+{
+
+	if (prov)
+		l_timeout_remove(prov->timeout);
+
+	mesh_send_cancel(bec_filter, sizeof(bec_filter));
+	mesh_send_cancel(&pkt_filter, sizeof(pkt_filter));
+
+	if (prov->trans_tx) {
+		if (prov->transport == PB_ADV)
+			pb_adv_unreg(prov->trans_data);
+	}
+
+	l_free(prov);
+	prov = NULL;
+}
+
+static void acp_prov_close(void *user_data, uint8_t reason)
+{
+	/* TODO: Handle Close */
+}
+
+static void prov_to(struct l_timeout *timeout, void *user_data)
+{
+	struct mesh_prov_acceptor *rx_prov = user_data;
+	uint8_t fail_code[2] = {PROV_FAILED, PROV_ERR_UNEXPECTED_ERR};
+
+	if (rx_prov != prov)
+		return;
+
+	prov->timeout = NULL;
+
+	if (prov->cmplt && prov->trans_tx) {
+		prov->cmplt(prov->caller_data, PROV_ERR_TIMEOUT, NULL);
+		prov->cmplt = NULL;
+		prov->trans_tx(prov->trans_data, fail_code, 2);
+		prov->timeout = l_timeout_create(1, prov_to, prov, NULL);
+		return;
+	}
+
+	acceptor_free();
+}
+
+static void acp_prov_open(void *user_data, prov_trans_tx_t trans_tx,
+				void *trans_data, uint8_t transport)
+{
+	struct mesh_prov_acceptor *rx_prov = user_data;
+
+	/* Only one provisioning session may be open at a time */
+	if (rx_prov != prov)
+		return;
+
+	/* Only one provisioning session may be open at a time */
+	if (prov->trans_tx && prov->trans_tx != trans_tx &&
+					prov->transport != transport)
+		return;
+
+	if (transport != PB_ADV)
+		return;
+
+	prov->trans_tx = trans_tx;
+	prov->transport = transport;
+	prov->trans_data = trans_data;
+	prov->timeout = l_timeout_create(prov->to_secs, prov_to, prov, NULL);
+}
+
+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 void prov_calc_secret(const uint8_t *pub, const uint8_t *priv,
+							uint8_t *secret)
+{
+	uint8_t tmp[64];
+
+	/* Convert to ECC byte order */
+	memcpy(tmp, pub, 64);
+	swap_u256_bytes(tmp);
+	swap_u256_bytes(tmp + 32);
+
+	ecdh_shared_secret(tmp, priv, secret);
+
+	/* Convert to Mesh byte order */
+	swap_u256_bytes(secret);
+}
+
+static void acp_credentials(struct mesh_prov_acceptor *prov)
+{
+	prov_calc_secret(prov->conf_inputs.prv_pub_key,
+			prov->private_key, prov->secret);
+
+	mesh_crypto_s1(&prov->conf_inputs,
+			sizeof(prov->conf_inputs), prov->salt);
+
+	mesh_crypto_prov_conf_key(prov->secret, prov->salt,
+			prov->calc_key);
+
+	l_getrandom(prov->rand_auth_workspace, 16);
+
+	print_packet("PublicKeyProv", prov->conf_inputs.prv_pub_key, 64);
+	print_packet("PublicKeyDev", prov->conf_inputs.dev_pub_key, 64);
+	print_packet("PrivateKeyLocal", prov->private_key, 32);
+	print_packet("ConfirmationInputs", &prov->conf_inputs,
+						sizeof(prov->conf_inputs));
+	print_packet("ECDHSecret", prov->secret, 32);
+	print_packet("LocalRandom", prov->rand_auth_workspace, 16);
+	print_packet("ConfirmationSalt", prov->salt, 16);
+	print_packet("ConfirmationKey", prov->calc_key, 16);
+}
+
+static uint32_t digit_mod(uint8_t power)
+{
+	uint32_t ret = 1;
+
+	while (power--)
+		ret *= 10;
+
+	return ret;
+}
+
+static void number_cb(void *user_data, int err, uint32_t number)
+{
+	struct mesh_prov_acceptor *rx_prov = user_data;
+	uint8_t out[2];
+
+	if (prov != rx_prov)
+		return;
+
+	if (err) {
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_UNEXPECTED_ERR;
+		prov->trans_tx(prov->trans_data, out, 2);
+		return;
+	}
+
+	/* Save two copies, to generate two confirmation values */
+	l_put_be32(number, prov->rand_auth_workspace + 28);
+	l_put_be32(number, prov->rand_auth_workspace + 44);
+	prov->material |= MAT_RAND_AUTH;
+	out[0] = PROV_INP_CMPLT;
+	prov->trans_tx(prov->trans_data, out, 1);
+}
+
+static void static_cb(void *user_data, int err, uint8_t *key, uint32_t len)
+{
+	struct mesh_prov_acceptor *rx_prov = user_data;
+	uint8_t out[2];
+
+	if (prov != rx_prov)
+		return;
+
+	if (err || !key || len != 16) {
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_UNEXPECTED_ERR;
+		prov->trans_tx(prov->trans_data, out, 2);
+		return;
+	}
+
+	/* Save two copies, to generate two confirmation values */
+	memcpy(prov->rand_auth_workspace + 16, key, 16);
+	memcpy(prov->rand_auth_workspace + 32, key, 16);
+	prov->material |= MAT_RAND_AUTH;
+}
+
+static void priv_key_cb(void *user_data, int err, uint8_t *key, uint32_t len)
+{
+	struct mesh_prov_acceptor *rx_prov = user_data;
+	uint8_t out[2];
+
+	if (prov != rx_prov)
+		return;
+
+	if (err || !key || len != 32) {
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_UNEXPECTED_ERR;
+		prov->trans_tx(prov->trans_data, out, 2);
+		return;
+	}
+
+	memcpy(prov->private_key, key, 32);
+	ecc_make_public_key(prov->private_key,
+			prov->conf_inputs.dev_pub_key);
+
+	/* Convert to Mesh byte order */
+	swap_u256_bytes(prov->conf_inputs.dev_pub_key);
+	swap_u256_bytes(prov->conf_inputs.dev_pub_key + 32);
+
+	prov->material |= MAT_LOCAL_PRIVATE;
+	if ((prov->material & MAT_SECRET) == MAT_SECRET)
+		acp_credentials(prov);
+}
+
+static void acp_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
+{
+	struct mesh_prov_acceptor *rx_prov = user_data;
+	struct mesh_prov_node_info *info;
+	uint8_t *out;
+	uint8_t type = *data++;
+	uint8_t fail_code[2];
+	uint32_t oob_key;
+	uint64_t decode_mic;
+	bool result;
+
+	if (rx_prov != prov || !prov->trans_tx)
+		return;
+
+	l_debug("Provisioning packet received type: %2.2x (%u octets)",
+								type, len);
+
+	if (type == prov->previous) {
+		l_error("Ignore repeated %2.2x packet", type);
+		return;
+	} else if (type > prov->expected || type < prov->previous) {
+		l_error("Expected %2.2x, Got:%2.2x", prov->expected, type);
+		fail_code[1] = PROV_ERR_UNEXPECTED_PDU;
+		goto failure;
+	}
+
+	if (type >= L_ARRAY_SIZE(expected_pdu_size) ||
+					len != expected_pdu_size[type]) {
+		l_error("Expected PDU size %d, Got %d (type: %2.2x)",
+			len, expected_pdu_size[type], type);
+		fail_code[1] = PROV_ERR_INVALID_FORMAT;
+		goto failure;
+	}
+
+	switch (type){
+	case PROV_INVITE: /* Prov Invite */
+		/* Prov Capabilities */
+		out = l_malloc(1 + sizeof(struct mesh_net_prov_caps));
+		out[0] = PROV_CAPS;
+		memcpy(out + 1, &prov->conf_inputs.caps,
+					sizeof(prov->conf_inputs.caps));
+
+		prov->conf_inputs.invite.attention = data[0];
+
+		prov->state = ACP_PROV_CAPS_SENT;
+		prov->expected = PROV_START;
+		prov->trans_tx(prov->trans_data,
+				out, sizeof(prov->conf_inputs.caps) + 1);
+		l_free(out);
+		break;
+
+	case PROV_START: /* Prov Start */
+		memcpy(&prov->conf_inputs.start, data,
+				sizeof(prov->conf_inputs.start));
+
+		if (prov->conf_inputs.start.algorithm ||
+				prov->conf_inputs.start.pub_key > 1 ||
+				prov->conf_inputs.start.auth_method > 3) {
+			fail_code[1] = PROV_ERR_INVALID_FORMAT;
+			goto failure;
+		}
+
+		if (prov->conf_inputs.start.pub_key) {
+			if (prov->conf_inputs.caps.pub_type) {
+				/* Prompt Agent for Private Key of OOB */
+				mesh_agent_request_private_key(prov->agent,
+							priv_key_cb, prov);
+			} else {
+				fail_code[1] = PROV_ERR_INVALID_PDU;
+				goto failure;
+			}
+		} else {
+			/* Ephemeral Public Key requested */
+			ecc_make_key(prov->conf_inputs.dev_pub_key,
+					prov->private_key);
+			swap_u256_bytes(prov->conf_inputs.dev_pub_key);
+			swap_u256_bytes(prov->conf_inputs.dev_pub_key + 32);
+			prov->material |= MAT_LOCAL_PRIVATE;
+		}
+
+		prov->expected = PROV_PUB_KEY;
+		break;
+
+	case PROV_PUB_KEY: /* Public Key */
+		/* Save Key */
+		memcpy(prov->conf_inputs.prv_pub_key, data, 64);
+		prov->material |= MAT_REMOTE_PUBLIC;
+		prov->expected = PROV_CONFIRM;
+
+		if ((prov->material & MAT_SECRET) != MAT_SECRET)
+			return;
+
+		acp_credentials(prov);
+
+		if (!prov->conf_inputs.start.pub_key) {
+			out = l_malloc(65);
+			out[0] = PROV_PUB_KEY;
+			memcpy(out + 1, prov->conf_inputs.dev_pub_key, 64);
+			prov->trans_tx(prov->trans_data, out, 65);
+			l_free(out);
+		}
+
+		/* Start Step 3 */
+		switch (prov->conf_inputs.start.auth_method) {
+		default:
+		case 0:
+			/* Auth Type 3c - No OOB */
+			break;
+
+		case 1:
+			/* Auth Type 3c - Static OOB */
+			/* Prompt Agent for Static OOB */
+			fail_code[1] = mesh_agent_request_static(prov->agent,
+					static_cb, prov);
+
+			if (fail_code[1])
+				goto failure;
+
+			break;
+
+		case 2:
+			/* Auth Type 3a - Output OOB */
+			l_getrandom(&oob_key, sizeof(oob_key));
+			oob_key %= digit_mod(prov->conf_inputs.start.auth_size);
+
+			/* Save two copies, for two confirmation values */
+			l_put_be32(oob_key, prov->rand_auth_workspace + 28);
+			l_put_be32(oob_key, prov->rand_auth_workspace + 44);
+			prov->material |= MAT_RAND_AUTH;
+
+			if (prov->conf_inputs.start.auth_action ==
+							PROV_ACTION_OUT_ALPHA) {
+				/* TODO: Construst NUL-term string to pass */
+				fail_code[1] = mesh_agent_display_string(
+					prov->agent, NULL, NULL, prov);
+			} else {
+				/* Ask Agent to Display U32 */
+				fail_code[1] = mesh_agent_display_number(
+					prov->agent, false,
+					prov->conf_inputs.start.auth_action,
+					oob_key, NULL, prov);
+			}
+
+			if (fail_code[1])
+				goto failure;
+
+			break;
+
+		case 3:
+			/* Auth Type 3b - input OOB */
+			/* Prompt Agent for Input OOB */
+			if (prov->conf_inputs.start.auth_action ==
+							PROV_ACTION_IN_ALPHA) {
+				fail_code[1] = mesh_agent_prompt_alpha(
+					prov->agent,
+					static_cb, prov);
+			} else {
+				fail_code[1] = mesh_agent_prompt_number(
+					prov->agent, false,
+					prov->conf_inputs.start.auth_action,
+					number_cb, prov);
+			}
+
+			if (fail_code[1])
+				goto failure;
+
+			break;
+		}
+
+		prov->expected = PROV_CONFIRM;
+		break;
+
+	case PROV_CONFIRM: /* Confirmation */
+		out = l_malloc(17);
+		out[0] = PROV_CONFIRM;
+
+		/* Calculate and Send our Confirmation */
+		mesh_crypto_aes_cmac(prov->calc_key, prov->rand_auth_workspace,
+								32, out + 1);
+		prov->trans_tx(prov->trans_data, out, 17);
+		l_free(out);
+
+		/* Save Provisioners confirmation for later compare */
+		memcpy(prov->confirm, data, 16);
+		prov->expected = PROV_RANDOM;
+		break;
+
+	case PROV_RANDOM: /* Random Value */
+		out = l_malloc(17);
+		/* Calculate Session key (needed later) while data is fresh */
+		mesh_crypto_prov_prov_salt(prov->salt, data,
+						prov->rand_auth_workspace,
+						prov->salt);
+		mesh_crypto_session_key(prov->secret, prov->salt, prov->s_key);
+		mesh_crypto_nonce(prov->secret, prov->salt, prov->s_nonce);
+
+		/* Calculate expected Provisioner Confirm */
+		memcpy(prov->rand_auth_workspace + 16, data, 16);
+		mesh_crypto_aes_cmac(prov->calc_key,
+				prov->rand_auth_workspace + 16, 32, out);
+
+		/* Compare our calculation with Provisioners */
+		if (memcmp(out, prov->confirm, 16)) {
+			fail_code[1] = PROV_ERR_CONFIRM_FAILED;
+			l_free(out);
+			goto failure;
+		}
+
+		/* Send Random value we used */
+		out[0] = PROV_RANDOM;
+		memcpy(out + 1, prov->rand_auth_workspace, 16);
+		prov->trans_tx(prov->trans_data, out, 17);
+		l_free(out);
+		prov->expected = PROV_DATA;
+		break;
+
+	case PROV_DATA: /* Provisioning Data */
+
+		/* Calculate our device key */
+		mesh_crypto_device_key(prov->secret,
+				prov->salt,
+				prov->calc_key);
+
+		/* Decrypt new node data into workspace */
+		mesh_crypto_aes_ccm_decrypt(prov->s_nonce, prov->s_key,
+				NULL, 0,
+				data, len - 1, prov->rand_auth_workspace,
+				&decode_mic, sizeof(decode_mic));
+
+		/* Validate that the data hasn't been messed with in transit */
+		if (l_get_be64(data + 25) != decode_mic) {
+			l_error("Provisioning Failed-MIC compare");
+			fail_code[1] = PROV_ERR_DECRYPT_FAILED;
+			goto failure;
+		}
+
+		info = l_malloc(sizeof(struct mesh_prov_node_info));
+
+		memcpy(info->device_key, prov->calc_key, 16);
+		memcpy(info->net_key, prov->rand_auth_workspace, 16);
+		info->net_index = l_get_be16(prov->rand_auth_workspace + 16);
+		info->flags = prov->rand_auth_workspace[18];
+		info->iv_index = l_get_be32(prov->rand_auth_workspace + 19);
+		info->unicast = l_get_be16(prov->rand_auth_workspace + 23);
+
+		result = prov->cmplt(prov->caller_data, PROV_ERR_SUCCESS, info);
+		prov->cmplt = NULL;
+		l_free(info);
+
+		if (result) {
+			prov->rand_auth_workspace[0] = PROV_COMPLETE;
+			prov->trans_tx(prov->trans_data,
+					prov->rand_auth_workspace, 1);
+			goto cleanup;
+		} else {
+			fail_code[1] = PROV_ERR_UNEXPECTED_ERR;
+			goto failure;
+		}
+		break;
+
+	case PROV_FAILED: /* Provisioning Error -- abort */
+		/* TODO: Call Complete Callback (Fail)*/
+		prov->cmplt(prov->caller_data,
+				data[0] ? data[0] : PROV_ERR_UNEXPECTED_ERR,
+				NULL);
+		prov->cmplt = NULL;
+		goto cleanup;
+	}
+
+	prov->previous = type;
+	return;
+
+failure:
+	fail_code[0] = PROV_FAILED;
+	prov->trans_tx(prov->trans_data, fail_code, 2);
+	if (prov->cmplt)
+		prov->cmplt(prov->caller_data, fail_code[1], NULL);
+	prov->cmplt = NULL;
+
+cleanup:
+	l_timeout_remove(prov->timeout);
+
+	/* Give PB Link 5 seconds to end session */
+	prov->timeout = l_timeout_create(5, prov_to, prov, NULL);
+}
+
+static void acp_prov_ack(void *user_data, uint8_t msg_num)
+{
+	/* TODO: Handle PB-ADV Ack */
+}
+
+
+/* This starts unprovisioned device beacon */
+bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
+		uint16_t algorithms, uint32_t timeout,
+		struct mesh_agent *agent,
+		mesh_prov_acceptor_complete_func_t complete_cb,
+		void *caller_data)
+{
+	struct mesh_agent_prov_caps *caps;
+	uint8_t beacon[24] = {MESH_AD_TYPE_BEACON,
+						BEACON_TYPE_UNPROVISIONED};
+	uint8_t len = sizeof(beacon) - sizeof(uint32_t);
+	bool result;
+
+	/* Invoked from Join() method in mesh-api.txt, to join a
+	 * remote mesh network.
+	 */
+
+	if (prov)
+		return false;
+
+	prov = l_new(struct mesh_prov_acceptor, 1);
+	prov->to_secs = timeout;
+	prov->agent = agent;
+	prov->cmplt = complete_cb;
+	prov->previous = -1;
+	prov->caller_data = caller_data;
+
+	caps = mesh_agent_get_caps(agent);
+
+	/* TODO: Should we sanity check values here or elsewhere? */
+	prov->conf_inputs.caps.num_ele = num_ele;
+	prov->conf_inputs.caps.pub_type = caps->pub_type;
+	prov->conf_inputs.caps.static_type = caps->static_type;
+	prov->conf_inputs.caps.output_size = caps->output_size;
+	prov->conf_inputs.caps.input_size = caps->input_size;
+
+	/* Store UINT16 values in Over-the-Air order, in packed structure
+	 * for crypto inputs
+	 */
+	l_put_be16(algorithms, &prov->conf_inputs.caps.algorithms);
+	l_put_be16(caps->output_action, &prov->conf_inputs.caps.output_action);
+	l_put_be16(caps->input_action, &prov->conf_inputs.caps.input_action);
+
+	/* Compose Unprovisioned Beacon */
+	memcpy(beacon + 2, uuid, 16);
+	l_put_be16(caps->oob_info, beacon + 18);
+	if (caps->oob_info & OOB_INFO_URI_HASH){
+		l_put_be32(caps->uri_hash, beacon + 20);
+		len += sizeof(uint32_t);
+	}
+
+	/* Infinitely Beacon until Canceled, or Provisioning Starts */
+	result = mesh_send_pkt(0, 500, beacon, len);
+
+	if (!result)
+		goto error_fail;
+
+	/* Always register for PB-ADV */
+	result = pb_adv_reg(acp_prov_open, acp_prov_close, acp_prov_rx,
+						acp_prov_ack, uuid, prov);
+
+	if (result)
+		return true;
+
+error_fail:
+	acceptor_free();
+	return false;
+}
+
+void acceptor_cancel(void *user_data)
+{
+	acceptor_free();
+}
-- 
2.14.5


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

* [PATCH BlueZ v6 09/26] mesh: Initiator side provisioning implementation
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (7 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 08/26] mesh: Acceptor side provisioning implementation Brian Gix
@ 2018-12-28 22:07 ` " Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 10/26] mesh: Rewrite Controler interface for full init Brian Gix
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

This functionaility (originally found in provision.c) was rewritten
as the Initiator side only of the provisioning procedure.  If the
local device owns and controls access to a mesh network, this is the
side of the provisioning procedure that it must use to bring new
unprovisioned devices into the network as Nodes.
---
 mesh/prov-initiator.c | 641 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 641 insertions(+)
 create mode 100644 mesh/prov-initiator.c

diff --git a/mesh/prov-initiator.c b/mesh/prov-initiator.c
new file mode 100644
index 000000000..669cf340d
--- /dev/null
+++ b/mesh/prov-initiator.c
@@ -0,0 +1,641 @@
+/*
+ *
+ *  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 "mesh/mesh-defs.h"
+#include "src/shared/ecc.h"
+
+#include "mesh/util.h"
+#include "mesh/net_keys.h"
+#include "mesh/crypto.h"
+#include "mesh/net.h"
+#include "mesh/error.h"
+#include "mesh/prov.h"
+#include "mesh/provision.h"
+#include "mesh/pb-adv.h"
+#include "mesh/mesh.h"
+#include "mesh/agent.h"
+
+/* Quick size sanity check */
+static const uint16_t expected_pdu_size[] = {
+	2,	/* PROV_INVITE */
+	12,	/* PROV_CAPS */
+	6,	/* PROV_START */
+	65,	/* PROV_PUB_KEY */
+	1,	/* PROV_INP_CMPLT */
+	17,	/* PROV_CONFIRM */
+	17,	/* PROV_RANDOM */
+	34,	/* PROV_DATA */
+	1,	/* PROV_COMPLETE */
+	2,	/* PROV_FAILED */
+};
+
+#define BEACON_TYPE_UNPROVISIONED		0x00
+
+static const uint8_t pkt_filter = MESH_AD_TYPE_PROVISION;
+
+enum int_state {
+	INT_PROV_IDLE = 0,
+	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,
+};
+
+#define MAT_REMOTE_PUBLIC	0x01
+#define MAT_LOCAL_PRIVATE	0x02
+#define MAT_RAND_AUTH		0x04
+#define MAT_SECRET	(MAT_REMOTE_PUBLIC | MAT_LOCAL_PRIVATE)
+
+struct mesh_prov_initiator {
+	mesh_prov_initiator_complete_func_t cmplt;
+	prov_trans_tx_t trans_tx;
+	void *agent;
+	void *caller_data;
+	void *trans_data;
+	struct l_timeout *timeout;
+	uint32_t to_secs;
+	enum int_state	state;
+	enum trans_type transport;
+	uint8_t material;
+	uint8_t expected;
+	int8_t previous;
+	struct conf_input conf_inputs;
+	uint8_t calc_key[16];
+	uint8_t salt[16];
+	uint8_t confirm[16];
+	uint8_t s_key[16];
+	uint8_t s_nonce[13];
+	uint8_t private_key[32];
+	uint8_t secret[32];
+	uint8_t rand_auth_workspace[48];
+};
+
+static struct mesh_prov_initiator *prov = NULL;
+
+static void initiator_free(void)
+{
+
+	if (prov)
+		l_timeout_remove(prov->timeout);
+
+	mesh_send_cancel(&pkt_filter, sizeof(pkt_filter));
+
+	l_free(prov);
+	prov = NULL;
+}
+
+static void int_prov_close(void *user_data, uint8_t reason)
+{
+	/* TODO: Handle Close */
+}
+
+static void int_prov_open(void *user_data, prov_trans_tx_t trans_tx,
+				void *trans_data, uint8_t transport)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t invite[] = { PROV_INVITE, 30 };
+
+	/* Only one provisioning session may be open at a time */
+	if (rx_prov != prov)
+		return;
+
+	/* Only one provisioning session may be open at a time */
+	if (prov->trans_tx && prov->trans_tx != trans_tx &&
+					prov->transport != transport)
+		return;
+
+	/* We only care here if transport does *not* match */
+	if (transport != prov->transport)
+		return;
+
+	/* Always use an ephemeral key when Initiator */
+	ecc_make_key(prov->conf_inputs.prv_pub_key, prov->private_key);
+	prov->material |= MAT_LOCAL_PRIVATE;
+
+	prov->trans_tx = trans_tx;
+	prov->trans_data = trans_data;
+	prov->state = INT_PROV_INVITE_SENT;
+	prov->expected = PROV_CAPS;
+
+	prov->conf_inputs.invite.attention = invite[1];
+	prov->trans_tx(prov->trans_data, invite, sizeof(invite));
+	return;
+}
+
+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 void prov_calc_secret(const uint8_t *pub, const uint8_t *priv,
+							uint8_t *secret)
+{
+	uint8_t tmp[64];
+
+	/* Convert to ECC byte order */
+	memcpy(tmp, pub, 64);
+	swap_u256_bytes(tmp);
+	swap_u256_bytes(tmp + 32);
+
+	ecdh_shared_secret(tmp, priv, secret);
+
+	/* Convert to Mesh byte order */
+	swap_u256_bytes(secret);
+}
+
+static void int_credentials(struct mesh_prov_initiator *prov)
+{
+	prov_calc_secret(prov->conf_inputs.dev_pub_key,
+			prov->private_key, prov->secret);
+
+	mesh_crypto_s1(&prov->conf_inputs,
+			sizeof(prov->conf_inputs), prov->salt);
+
+	mesh_crypto_prov_conf_key(prov->secret, prov->salt,
+			prov->calc_key);
+
+	l_getrandom(prov->rand_auth_workspace, 16);
+
+	print_packet("PublicKeyProv", prov->conf_inputs.prv_pub_key, 64);
+	print_packet("PublicKeyDev", prov->conf_inputs.dev_pub_key, 64);
+	print_packet("PrivateKeyLocal", prov->private_key, 32);
+	print_packet("ConfirmationInputs", &prov->conf_inputs,
+						sizeof(prov->conf_inputs));
+	print_packet("ECDHSecret", prov->secret, 32);
+	print_packet("LocalRandom", prov->rand_auth_workspace, 16);
+	print_packet("ConfirmationSalt", prov->salt, 16);
+	print_packet("ConfirmationKey", prov->calc_key, 16);
+}
+
+static uint8_t u16_high_bit(uint16_t mask)
+{
+	uint8_t cnt = 0;
+
+	if (!mask)
+		return 0xff;
+
+	while (mask & 0xfffe) {
+		cnt++;
+		mask >>= 1;
+	}
+
+	return cnt;
+}
+
+static uint32_t digit_mod(uint8_t power)
+{
+	uint32_t ret = 1;
+
+	while (power--)
+		ret *= 10;
+
+	return ret;
+}
+
+static void calc_local_material(const uint8_t *random)
+{
+	/* Calculate SessionKey while the data is fresh */
+	mesh_crypto_prov_prov_salt(prov->salt,
+			prov->rand_auth_workspace, random,
+			prov->salt);
+	mesh_crypto_session_key(prov->secret, prov->salt,
+			prov->s_key);
+	mesh_crypto_nonce(prov->secret, prov->salt, prov->s_nonce);
+
+	print_packet("SessionKey", prov->s_key, sizeof(prov->s_key));
+	print_packet("Nonce", prov->s_nonce, sizeof(prov->s_nonce));
+	print_packet("RandomDevice", prov->rand_auth_workspace, 16);
+}
+
+static void number_cb(void *user_data, int err, uint32_t number)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t out[2];
+
+	if (prov != rx_prov)
+		return;
+
+	if (err) {
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_UNEXPECTED_ERR;
+		prov->trans_tx(prov->trans_data, out, 2);
+		return;
+	}
+
+	/* Save two copies, to generate two confirmation values */
+	l_put_be32(number, prov->rand_auth_workspace + 28);
+	l_put_be32(number, prov->rand_auth_workspace + 44);
+	prov->material |= MAT_RAND_AUTH;
+}
+
+static void static_cb(void *user_data, int err, uint8_t *key, uint32_t len)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t out[2];
+
+	if (prov != rx_prov)
+		return;
+
+	if (err || !key || len != 16) {
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_UNEXPECTED_ERR;
+		prov->trans_tx(prov->trans_data, out, 2);
+		return;
+	}
+
+	memcpy(prov->rand_auth_workspace + 16, key, 16);
+	memcpy(prov->rand_auth_workspace + 32, key, 16);
+	prov->material |= MAT_RAND_AUTH;
+}
+
+static void pub_key_cb(void *user_data, int err, uint8_t *key, uint32_t len)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t out[2];
+
+	if (prov != rx_prov)
+		return;
+
+	if (err || !key || len != 64) {
+		out[0] = PROV_FAILED;
+		out[1] = PROV_ERR_UNEXPECTED_ERR;
+		prov->trans_tx(prov->trans_data, out, 2);
+		return;
+	}
+
+		memcpy(prov->conf_inputs.dev_pub_key, key, 64);
+		prov->material |= MAT_REMOTE_PUBLIC;
+
+		if ((prov->material & MAT_SECRET) == MAT_SECRET)
+			int_credentials(prov);
+}
+
+static void int_prov_rx(void *user_data, const uint8_t *data, uint16_t len)
+{
+	struct mesh_prov_initiator *rx_prov = user_data;
+	uint8_t *out;
+	uint8_t type = *data++;
+	uint8_t fail_code[2];
+	uint32_t oob_key;
+	uint64_t mic;
+
+	if (rx_prov != prov || !prov->trans_tx)
+		return;
+
+	l_debug("Provisioning packet received type: %2.2x (%u octets)",
+								type, len);
+
+	if (type == prov->previous) {
+		l_error("Ignore repeated %2.2x packet", type);
+		return;
+	} else if (type > prov->expected || type < prov->previous) {
+		l_error("Expected %2.2x, Got:%2.2x", prov->expected, type);
+		fail_code[1] = PROV_ERR_UNEXPECTED_PDU;
+		goto failure;
+	}
+
+	if (type >= L_ARRAY_SIZE(expected_pdu_size) ||
+					len != expected_pdu_size[type]) {
+		l_error("Expected PDU size %d, Got %d (type: %2.2x)",
+			len, expected_pdu_size[type], type);
+		fail_code[1] = PROV_ERR_INVALID_FORMAT;
+		goto failure;
+	}
+
+	switch (type) {
+	case PROV_CAPS: /* Capabilities */
+		prov->state = INT_PROV_INVITE_ACKED;
+		memcpy(&prov->conf_inputs.caps, data,
+					sizeof(prov->conf_inputs.caps));
+
+		l_debug("Got Num Ele %d", data[0]);
+		l_debug("Got alg %4.4x", l_get_be16(data + 1));
+		l_debug("Got pub_type %d", data[3]);
+		l_debug("Got static_type %d", data[4]);
+		l_debug("Got output_size %d", data[5]);
+		l_debug("Got output_action %d", l_get_be16(data + 6));
+		l_debug("Got input_size %d", data[8]);
+		l_debug("Got input_action %d", l_get_be16(data + 9));
+
+		if (!(l_get_be16(data + 1) & 0x0001)) {
+			l_error("Unsupported Algorithm");
+			fail_code[1] = PROV_ERR_INVALID_FORMAT;
+			goto failure;
+		}
+
+		/* If Public Key available Out of Band, use it */
+		if (prov->conf_inputs.caps.pub_type) {
+			prov->conf_inputs.start.pub_key = 0x01;
+			prov->expected = PROV_CONFIRM;
+			/* Prompt Agent for remote Public Key */
+			mesh_agent_request_public_key(prov->agent,
+							pub_key_cb, prov);
+
+			/* Nothing else for us to do now */
+		} else
+			prov->expected = PROV_PUB_KEY;
+
+		/* Parse OOB Options, prefer static, then out, then in */
+		if (prov->conf_inputs.caps.static_type) {
+
+			prov->conf_inputs.start.auth_method = 0x01;
+
+		} else if (prov->conf_inputs.caps.output_size &&
+				prov->conf_inputs.caps.output_action) {
+
+			prov->conf_inputs.start.auth_method = 0x02;
+			prov->conf_inputs.start.auth_action =
+					u16_high_bit(l_get_be16(data + 6));
+			prov->conf_inputs.start.auth_size =
+						(data[5] > 8 ? 8 : data[5]);
+
+		} else if (prov->conf_inputs.caps.input_size &&
+				prov->conf_inputs.caps.input_action) {
+
+			prov->conf_inputs.start.auth_method = 0x03;
+			prov->conf_inputs.start.auth_action =
+					u16_high_bit(l_get_be16(data + 9));
+			prov->conf_inputs.start.auth_size =
+						(data[8] > 8 ? 8 : data[8]);
+
+		}
+
+		out = l_malloc(1 + sizeof(prov->conf_inputs.start));
+		out[0] = PROV_START;
+		memcpy(out + 1, &prov->conf_inputs.start,
+					sizeof(prov->conf_inputs.start));
+
+		prov->state = INT_PROV_START_SENT;
+		prov->trans_tx(prov->trans_data, out,
+					sizeof(prov->conf_inputs.start) + 1);
+		l_free(out);
+		break;
+
+	case PROV_PUB_KEY: /* Public Key */
+		/* If we expected Pub Key Out-Of-Band, then fail */
+		if (prov->conf_inputs.start.pub_key) {
+			fail_code[1] = PROV_ERR_INVALID_PDU;
+			goto failure;
+		}
+
+		memcpy(prov->conf_inputs.dev_pub_key, data, 64);
+		prov->material |= MAT_REMOTE_PUBLIC;
+		prov->expected = PROV_CONFIRM;
+
+		if ((prov->material & MAT_SECRET) != MAT_SECRET)
+			return;
+
+		int_credentials(prov);
+		prov->state = INT_PROV_KEY_ACKED;
+
+		prov->expected = PROV_CONFIRM;
+
+		memset(prov->rand_auth_workspace + 16, 0, 32);
+		switch (prov->conf_inputs.start.auth_method) {
+		default:
+		case 0:
+			/* Auth Type 3c - No OOB */
+			prov->material |= MAT_RAND_AUTH;
+			break;
+		case 1:
+			/* Auth Type 3c - Static OOB */
+			/* Prompt Agent for Static OOB */
+			fail_code[1] = mesh_agent_request_static(prov->agent,
+					static_cb, prov);
+
+			if (fail_code[1])
+				goto failure;
+
+			break;
+		case 2:
+			/* Auth Type 3a - Output OOB */
+			/* Prompt Agent for Output OOB */
+			if (prov->conf_inputs.start.auth_action ==
+							PROV_ACTION_OUT_ALPHA) {
+				fail_code[1] = mesh_agent_prompt_alpha(
+					prov->agent,
+					static_cb, prov);
+			} else {
+				fail_code[1] = mesh_agent_prompt_number(
+					prov->agent, true,
+					prov->conf_inputs.start.auth_action,
+					number_cb, prov);
+			}
+
+			if (fail_code[1])
+				goto failure;
+
+			break;
+
+
+		case 3:
+			/* Auth Type 3b - input OOB */
+			l_getrandom(&oob_key, sizeof(oob_key));
+			oob_key %= digit_mod(prov->conf_inputs.start.auth_size);
+
+			/* Save two copies, for two confirmation values */
+			l_put_be32(oob_key, prov->rand_auth_workspace + 28);
+			l_put_be32(oob_key, prov->rand_auth_workspace + 44);
+			prov->material |= MAT_RAND_AUTH;
+			/* Ask Agent to Display U32 */
+			if (prov->conf_inputs.start.auth_action ==
+							PROV_ACTION_IN_ALPHA) {
+				/* TODO: Construst NUL-term string to pass */
+				fail_code[1] = mesh_agent_display_string(
+					prov->agent, NULL, NULL, prov);
+			} else {
+				fail_code[1] = mesh_agent_display_number(
+					prov->agent, false,
+					prov->conf_inputs.start.auth_action,
+					oob_key, NULL, prov);
+			}
+
+			if (fail_code[1])
+				goto failure;
+
+			break;
+
+
+		}
+		break;
+
+	case PROV_INP_CMPLT: /* Provisioning Input Complete */
+		/* TODO: Cancel Agent prompt */
+		prov->expected = PROV_CONFIRM;
+		out = l_malloc(17);
+		out[0] = PROV_CONFIRM;
+		mesh_crypto_aes_cmac(prov->calc_key, prov->rand_auth_workspace,
+								32, out + 1);
+		prov->trans_tx(prov->trans_data, out, 17);
+		l_free(out);
+		break;
+
+	case PROV_CONFIRM: /* Confirmation */
+		prov->state = INT_PROV_CONF_ACKED;
+		/* RXed Device Confirmation */
+		memcpy(prov->confirm, data, 16);
+		print_packet("ConfirmationDevice", prov->confirm, 16);
+		prov->expected = PROV_RANDOM;
+		out = l_malloc(17);
+		out[0] = PROV_RANDOM;
+		memcpy(out + 1, prov->rand_auth_workspace, 16);
+		prov->trans_tx(prov->trans_data, out, 17);
+		l_free(out);
+		break;
+
+	case PROV_RANDOM: /* Random */
+		prov->state = INT_PROV_RAND_ACKED;
+
+		/* RXed Device Confirmation */
+		memcpy(prov->rand_auth_workspace + 16, data, 16);
+		print_packet("RandomDevice", data, 16);
+		calc_local_material(data);
+
+		mesh_crypto_aes_cmac(prov->calc_key,
+						prov->rand_auth_workspace + 16,
+						32, prov->rand_auth_workspace);
+
+		if (memcmp(prov->rand_auth_workspace, prov->confirm, 16)) {
+			l_error("Provisioning Failed-Confirm compare)");
+			fail_code[1] = PROV_ERR_CONFIRM_FAILED;
+			goto failure;
+		}
+
+		if (prov->state == INT_PROV_RAND_ACKED) {
+			prov->expected = PROV_COMPLETE;
+			out = l_malloc(34);
+			out[0] = PROV_DATA;
+			/* TODO: Fill Prov Data Structure */
+			/* Encrypt Prov Data */
+			mesh_crypto_aes_ccm_encrypt(prov->s_nonce, prov->s_key,
+					NULL, 0,
+					out + 1,
+					25,
+					out + 1,
+					&mic, sizeof(mic));
+			prov->trans_tx(prov->trans_data, out, 34);
+			l_free(out);
+		}
+		break;
+
+	case PROV_COMPLETE: /* Complete */
+		l_info("Provisioning Complete");
+		prov->state = INT_PROV_IDLE;
+		//mesh_prov_close(prov, 0);
+		break;
+
+	case PROV_FAILED: /* Failed */
+		l_error("Provisioning Failed (reason: %d)", data[0]);
+		//mesh_prov_close(prov, data[0]);
+		break;
+
+	default:
+		l_error("Unknown Pkt %2.2x", type);
+		fail_code[1] = PROV_ERR_UNEXPECTED_PDU;
+		goto failure;
+	}
+
+	prov->previous = type;
+	return;
+
+failure:
+	fail_code[0] = PROV_FAILED;
+	prov->trans_tx(prov->trans_data, fail_code, 2);
+	/* TODO: Call Complete Callback (Fail)*/
+}
+
+static void int_prov_ack(void *user_data, uint8_t msg_num)
+{
+	/* TODO: Handle PB-ADV Ack */
+}
+
+
+bool initiator_start(enum trans_type transport,
+		uint8_t uuid[16],
+		uint16_t max_ele,
+		uint16_t server, /* Only valid for PB-Remote */
+		uint32_t timeout, /* in seconds from mesh.conf */
+		struct mesh_agent *agent,
+		mesh_prov_initiator_complete_func_t complete_cb,
+		void *caller_data)
+{
+	bool result;
+
+	/* Invoked from Add() method in mesh-api.txt, to add a
+	 * remote unprovisioned device network.
+	 */
+
+	if (prov)
+		return false;
+
+	prov = l_new(struct mesh_prov_initiator, 1);
+	prov->to_secs = timeout;
+	prov->agent = agent;
+	prov->cmplt = complete_cb;
+	prov->caller_data = caller_data;
+	prov->previous = -1;
+
+	/* Always register for PB-ADV */
+	result = pb_adv_reg(int_prov_open, int_prov_close, int_prov_rx,
+						int_prov_ack, uuid, prov);
+
+	if (result)
+		return true;
+
+	initiator_free();
+	return false;
+}
+
+void initiator_cancel(void *user_data)
+{
+	initiator_free();
+}
-- 
2.14.5


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

* [PATCH BlueZ v6 10/26] mesh: Rewrite Controler interface for full init
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (8 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 09/26] mesh: Initiator " Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 11/26] mesh: Unchanged variables set to const Brian Gix
                   ` (15 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

Functionaility has been added to allow a BT 4.0 or later controller
to be used for Advertising based Mesh usage, regardless of how it
was previously initialized.
---
 mesh/mesh-io-generic.c | 149 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 138 insertions(+), 11 deletions(-)

diff --git a/mesh/mesh-io-generic.c b/mesh/mesh-io-generic.c
index 52514e280..48e47e7a4 100644
--- a/mesh/mesh-io-generic.c
+++ b/mesh/mesh-io-generic.c
@@ -30,7 +30,6 @@
 #include "monitor/bt.h"
 #include "src/shared/hci.h"
 
-#include "mesh/display.h"
 #include "mesh/mesh-io.h"
 #include "mesh/mesh-io-api.h"
 
@@ -177,6 +176,116 @@ static void event_callback(const void *buf, uint8_t size, void *user_data)
 	}
 }
 
+static void local_commands_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_local_commands *rsp = data;
+
+	if (rsp->status)
+		l_error("Failed to read local commands");
+}
+
+static void local_features_callback(const void *data, uint8_t size,
+							void *user_data)
+{
+	const struct bt_hci_rsp_read_local_features *rsp = data;
+
+	if (rsp->status)
+		l_error("Failed to read local features");
+}
+
+static void hci_generic_callback(const void *data, uint8_t size,
+								void *user_data)
+{
+	uint8_t status = l_get_u8(data);
+
+	if (status)
+		l_error("Failed to initialize HCI");
+}
+
+static void configure_hci(struct mesh_io_private *io)
+{
+	struct bt_hci_cmd_le_set_scan_parameters cmd;
+	struct bt_hci_cmd_set_event_mask cmd_sem;
+	struct bt_hci_cmd_le_set_event_mask cmd_slem;
+
+	/* Set scan parameters */
+	cmd.type = 0x00; /* Passive Scanning. No scanning PDUs shall be sent */
+	cmd.interval = 0x0030; /* Scan Interval = N * 0.625ms */
+	cmd.window = 0x0030; /* Scan Window = N * 0.625ms */
+	cmd.own_addr_type = 0x00; /* Public Device Address */
+	/* Accept all advertising packets except directed advertising packets
+	 * not addressed to this device (default).
+	 */
+	cmd.filter_policy = 0x00;
+
+	/* Set event mask
+	 *
+	 * Mask: 0x2000800002008890
+	 *   Disconnection Complete
+	 *   Encryption Change
+	 *   Read Remote Version Information Complete
+	 *   Hardware Error
+	 *   Data Buffer Overflow
+	 *   Encryption Key Refresh Complete
+	 *   LE Meta
+	 */
+	cmd_sem.mask[0] = 0x90;
+	cmd_sem.mask[1] = 0x88;
+	cmd_sem.mask[2] = 0x00;
+	cmd_sem.mask[3] = 0x02;
+	cmd_sem.mask[4] = 0x00;
+	cmd_sem.mask[5] = 0x80;
+	cmd_sem.mask[6] = 0x00;
+	cmd_sem.mask[7] = 0x20;
+
+	/* Set LE event mask
+	 *
+	 * Mask: 0x000000000000087f
+	 *   LE Connection Complete
+	 *   LE Advertising Report
+	 *   LE Connection Update Complete
+	 *   LE Read Remote Used Features Complete
+	 *   LE Long Term Key Request
+	 *   LE Remote Connection Parameter Request
+	 *   LE Data Length Change
+	 *   LE PHY Update Complete
+	 */
+	cmd_slem.mask[0] = 0x7f;
+	cmd_slem.mask[1] = 0x08;
+	cmd_slem.mask[2] = 0x00;
+	cmd_slem.mask[3] = 0x00;
+	cmd_slem.mask[4] = 0x00;
+	cmd_slem.mask[5] = 0x00;
+	cmd_slem.mask[6] = 0x00;
+	cmd_slem.mask[7] = 0x00;
+
+	/* TODO: Move to suitable place. Set suitable masks */
+	/* Reset Command */
+	bt_hci_send(io->hci, BT_HCI_CMD_RESET, NULL, 0, hci_generic_callback,
+								NULL, NULL);
+
+	/* Read local supported commands */
+	bt_hci_send(io->hci, BT_HCI_CMD_READ_LOCAL_COMMANDS, NULL, 0,
+					local_commands_callback, NULL, NULL);
+
+	/* Read local supported features */
+	bt_hci_send(io->hci, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+					local_features_callback, NULL, NULL);
+
+	/* Set event mask */
+	bt_hci_send(io->hci, BT_HCI_CMD_SET_EVENT_MASK, &cmd_sem,
+			sizeof(cmd_sem), hci_generic_callback, NULL, NULL);
+
+	/* Set LE event mask */
+	bt_hci_send(io->hci, BT_HCI_CMD_LE_SET_EVENT_MASK, &cmd_slem,
+			sizeof(cmd_slem), hci_generic_callback, NULL, NULL);
+
+	/* Scan Params */
+	bt_hci_send(io->hci, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS, &cmd,
+				sizeof(cmd), hci_generic_callback, NULL, NULL);
+}
+
 static bool dev_init(uint16_t index, struct mesh_io *io)
 {
 	struct mesh_io_private *tmp;
@@ -198,6 +307,8 @@ static bool dev_init(uint16_t index, struct mesh_io *io)
 	if (!tmp->hci)
 		goto fail;
 
+	configure_hci(tmp);
+
 	bt_hci_register(tmp->hci, BT_HCI_EVT_LE_META_EVENT,
 						event_callback, io, NULL);
 
@@ -480,7 +591,6 @@ static bool send_tx(struct mesh_io *io, struct mesh_io_send_info *info,
 	if (!info || !data || !len || len > sizeof(tx->pkt))
 		return false;
 
-
 	tx = l_new(struct tx_pkt, 1);
 	if (!tx)
 		return false;
@@ -524,7 +634,7 @@ static bool find_by_pattern(const void *a, const void *b)
 	return (!memcmp(tx->pkt, pattern->data, pattern->len));
 }
 
-static bool tx_cancel(struct mesh_io *io, uint8_t *data, uint8_t len)
+static bool tx_cancel(struct mesh_io *io, const uint8_t *data, uint8_t len)
 {
 	struct mesh_io_private *pvt = io->pvt;
 	struct tx_pkt *tx;
@@ -568,13 +678,25 @@ static bool find_by_filter_id(const void *a, const void *b)
 	return rx_reg->filter_id == filter_id;
 }
 
+static void set_recv_scan_enable(const void *buf, uint8_t size,
+							void *user_data)
+{
+	struct mesh_io_private *pvt = user_data;
+	struct bt_hci_cmd_le_set_scan_enable cmd;
+
+	cmd.enable = 0x01;	/* Enable scanning */
+	cmd.filter_dup = 0x00;	/* Report duplicates */
+	bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+			&cmd, sizeof(cmd), NULL, NULL, NULL);
+}
+
 static bool recv_register(struct mesh_io *io, uint8_t filter_id,
 				mesh_io_recv_func_t cb, void *user_data)
 {
-	struct bt_hci_cmd_le_set_scan_enable cmd;
+	struct bt_hci_cmd_le_set_scan_parameters cmd;
 	struct mesh_io_private *pvt = io->pvt;
 	struct pvt_rx_reg *rx_reg;
-	bool scanning;
+	bool already_scanning;
 
 	l_info("%s %d", __func__, filter_id);
 	if (!cb || !filter_id || filter_id > sizeof(pvt->filters))
@@ -593,15 +715,20 @@ static bool recv_register(struct mesh_io *io, uint8_t filter_id,
 	rx_reg->cb = cb;
 	rx_reg->user_data = user_data;
 
-	scanning = !l_queue_isempty(pvt->rx_regs);
+	already_scanning = !l_queue_isempty(pvt->rx_regs);
 
 	l_queue_push_head(pvt->rx_regs, rx_reg);
 
-	if (!scanning) {
-		cmd.enable = 0x01;	/* Enable scanning */
-		cmd.filter_dup = 0x00;	/* Report duplicates */
-		bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
-				&cmd, sizeof(cmd), NULL, NULL, NULL);
+	if (!already_scanning) {
+		cmd.type = 0x00;			/* Passive scanning */
+		cmd.interval = L_CPU_TO_LE16(0x0010);	/* 10 ms */
+		cmd.window = L_CPU_TO_LE16(0x0010);	/* 10 ms */
+		cmd.own_addr_type = 0x01;		/* ADDR_TYPE_RANDOM */
+		cmd.filter_policy = 0x00;		/* Accept all */
+
+		bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_PARAMETERS,
+				&cmd, sizeof(cmd),
+				set_recv_scan_enable, pvt, NULL);
 	}
 
 	return true;
-- 
2.14.5


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

* [PATCH BlueZ v6 11/26] mesh: Unchanged variables set to const
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (9 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 10/26] mesh: Rewrite Controler interface for full init Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 12/26] mesh: Hex-String manipulation, and debug logging Brian Gix
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

---
 mesh/mesh-io-api.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mesh/mesh-io-api.h b/mesh/mesh-io-api.h
index f69fceeb2..acf12445d 100644
--- a/mesh/mesh-io-api.h
+++ b/mesh/mesh-io-api.h
@@ -31,7 +31,7 @@ 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,
+typedef bool (*mesh_io_tx_cancel_t)(struct mesh_io *io, const uint8_t *pattern,
 								uint8_t len);
 
 struct mesh_io_api {
-- 
2.14.5


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

* [PATCH BlueZ v6 12/26] mesh: Hex-String manipulation, and debug logging
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (10 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 11/26] mesh: Unchanged variables set to const Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 13/26] mesh: re-arrange provisioning for DBus API Brian Gix
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

Centralized some frequently used functionaility for logging
and save/retrieve of long (128+ bit) hexidecimal data.
---
 mesh/util.c | 27 ++++++++++++++++++++++++++-
 mesh/util.h |  2 +-
 2 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/mesh/util.c b/mesh/util.c
index 2cdcdf37d..b3ce1ce5f 100644
--- a/mesh/util.c
+++ b/mesh/util.c
@@ -15,7 +15,6 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -26,10 +25,36 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <unistd.h>
+#include <termios.h>
 #include <time.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <ell/ell.h>
 
 #include "mesh/util.h"
 
+void print_packet(const char *label, const void *data, uint16_t size)
+{
+	struct timeval pkt_time;
+
+	gettimeofday(&pkt_time, NULL);
+
+	if (size > 0) {
+		char *str;
+
+		str = l_util_hexstring(data, size);
+		l_debug("%05d.%03d %s: %s",
+				(uint32_t) pkt_time.tv_sec % 100000,
+				(uint32_t) pkt_time.tv_usec/1000, label, str);
+		l_free(str);
+	} else
+		l_debug("%05d.%03d %s: empty",
+				(uint32_t) pkt_time.tv_sec % 100000,
+				(uint32_t) pkt_time.tv_usec/1000, label);
+}
+
 uint32_t get_timestamp_secs(void)
 {
 	struct timespec ts;
diff --git a/mesh/util.h b/mesh/util.h
index 61110104a..007ea368e 100644
--- a/mesh/util.h
+++ b/mesh/util.h
@@ -15,10 +15,10 @@
  *  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);
+void print_packet(const char *label, const void *data, uint16_t size);
-- 
2.14.5


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

* [PATCH BlueZ v6 13/26] mesh: re-arrange provisioning for DBus API
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (11 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 12/26] mesh: Hex-String manipulation, and debug logging Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 14/26] mesh: Re-architect " Brian Gix
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

Provisioning restructured so that it is a service of a high level
(DBus based) API, and may be used on potentially multiple
provisioning transports.
---
 mesh/prov.h      |  14 +++++--
 mesh/provision.h | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 112 insertions(+), 14 deletions(-)

diff --git a/mesh/prov.h b/mesh/prov.h
index 09fe6c3cd..61ec08e10 100644
--- a/mesh/prov.h
+++ b/mesh/prov.h
@@ -47,11 +47,17 @@ enum mesh_prov_mode {
 };
 
 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 (*prov_trans_tx_t)(void *trans_data, uint8_t *data, uint16_t len);
+typedef void (*mesh_prov_open_func_t)(void *user_data, prov_trans_tx_t trans_tx,
+					void *trans_data, uint8_t trans_type);
+
+typedef void (*mesh_prov_close_func_t)(void *user_data, uint8_t reason);
 typedef void (*mesh_prov_send_func_t)(bool success, struct mesh_prov *prov);
-typedef void (*mesh_prov_receive_func_t)(const void *data, uint16_t size,
-							struct mesh_prov *prov);
+typedef void (*mesh_prov_ack_func_t)(void *user_data, uint8_t msg_num);
+typedef void (*mesh_prov_receive_func_t)(void *user_data, const uint8_t *data,
+								uint16_t size);
+
 
 struct prov_invite {
 	uint8_t attention;
diff --git a/mesh/provision.h b/mesh/provision.h
index 0c59bf037..6b61a45be 100644
--- a/mesh/provision.h
+++ b/mesh/provision.h
@@ -17,14 +17,106 @@
  *
  */
 
+
+/*
+ * size: hard define (mesh.conf - OOB_NUMBEROOB_NUMBER)
+ *      oob size - 8 if alpha or numeric
+ *	else 1 if mask is non zero
+ *	else 0
+ */
+struct bt_mesh;
 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);
+struct mesh_agent;
+
+/* Provisioner Agent Response Types */
+#define OOB_CANCEL		0x00
+#define OOB_PRIV_KEY		0x01
+#define OOB_PUB_KEY		0x02
+#define OOB_NUMBER		0x03
+#define OOB_STATIC		0x04
+#define OOB_NUMBER_DISPLAY	0x05
+
+/* Spec defined Provisioning message types */
+#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
+
+/* Spec defined Error Codes */
+#define PROV_ERR_SUCCESS		0x00
+#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
+/* Internally generated Error Codes */
+#define PROV_ERR_TIMEOUT		0xFF
+
+/* Provisioner Action values */
+/* IN */
+#define PROV_ACTION_PUSH		0x00
+#define PROV_ACTION_TWIST		0x01
+#define PROV_ACTION_IN_NUMERIC		0x02
+#define PROV_ACTION_IN_ALPHA		0x03
+/* OUT */
+#define PROV_ACTION_BLINK		0x00
+#define PROV_ACTION_BEEP		0x01
+#define PROV_ACTION_VIBRATE		0x02
+#define PROV_ACTION_OUT_NUMERIC		0x03
+#define PROV_ACTION_OUT_ALPHA		0x04
+
+/* OOB_Info defines from Table 3.54 of Mesh profile Specification v1.0 */
+#define OOB_INFO_URI_HASH	0x0002
+
+/* PB_REMOTE not supported from unprovisioned state */
+enum trans_type {
+	PB_ADV = 0,
+	PB_GATT,
+};
+
+#define PROV_FLAG_KR	0x01
+#define PROV_FLAG_IVU	0x02
+
+struct mesh_prov_node_info {
+	uint32_t iv_index;
+	uint16_t unicast;
+	uint16_t net_index;
+	uint8_t net_key[16];
+	uint8_t device_key[16];
+	uint8_t flags; /* IVU and KR bits */
+};
+
+typedef bool (*mesh_prov_acceptor_complete_func_t)(void *user_data,
+					uint8_t status,
+					struct mesh_prov_node_info *info);
+
+typedef bool (*mesh_prov_initiator_complete_func_t)(void *user_data,
+					uint8_t status,
+					struct mesh_prov_node_info *info);
+
+/* This starts unprovisioned device beacon */
+bool acceptor_start(uint8_t num_ele, uint8_t uuid[16],
+			uint16_t algorithms, uint32_t timeout,
+			struct mesh_agent *agent,
+			mesh_prov_acceptor_complete_func_t complete_cb,
+			void *caller_data);
+void acceptor_cancel(void *user_data);
+
+bool initiator_start(enum trans_type transport,
+		uint8_t uuid[16],
+		uint16_t max_ele,
+		uint16_t server, /* Only valid for PB-Remote */
+		uint32_t timeout, /* in seconds from mesh.conf */
+		struct mesh_agent *agent,
+		mesh_prov_initiator_complete_func_t complete_cb,
+		void *caller_data);
+void initiator_cancel(void *user_data);
-- 
2.14.5


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

* [PATCH BlueZ v6 14/26] mesh: Re-architect for DBus API
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (12 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 13/26] mesh: re-arrange provisioning for DBus API Brian Gix
@ 2018-12-28 22:07 ` " Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 15/26] mesh: Multi node Config Server model Brian Gix
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

The mesh code controls how the main functionalities of the daemon
(Provisioning and Mesh Network Messaging) interact with the
bluetooth controllers that are available. Restructured so that
multiple nodes, on multiple networks, can share the same controllers.
---
 mesh/mesh.c | 626 ++++++++++++++++++++++++++++++++++++++++++++++--------------
 mesh/mesh.h |  28 ++-
 2 files changed, 503 insertions(+), 151 deletions(-)

diff --git a/mesh/mesh.c b/mesh/mesh.c
index d1d409672..169e6f42c 100644
--- a/mesh/mesh.c
+++ b/mesh/mesh.c
@@ -15,7 +15,6 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -23,22 +22,33 @@
 #endif
 
 #define _GNU_SOURCE
-#include <time.h>
 #include <ell/ell.h>
+#include <json-c/json.h>
 
 #include "lib/bluetooth.h"
 #include "lib/mgmt.h"
 
 #include "src/shared/mgmt.h"
 
-#include "mesh/mesh-defs.h"
 #include "mesh/mesh-io.h"
 #include "mesh/node.h"
 #include "mesh/net.h"
 #include "mesh/storage.h"
-#include "mesh/cfgmod.h"
+#include "mesh/provision.h"
 #include "mesh/model.h"
+#include "mesh/dbus.h"
+#include "mesh/error.h"
 #include "mesh/mesh.h"
+#include "mesh/agent.h"
+
+/*
+ * The default values for mesh configuration. Can be
+ * overwritten by values from mesh.conf
+ */
+#define DEFAULT_PROV_TIMEOUT 60
+#define DEFAULT_ALGORITHMS 0x0001
+
+/* TODO: add more default values */
 
 struct scan_filter {
 	uint8_t id;
@@ -46,42 +56,49 @@ struct scan_filter {
 };
 
 struct bt_mesh {
-	struct mesh_net *net;
 	struct mesh_io *io;
 	struct l_queue *filters;
-	int ref_count;
-	uint16_t index;
+	prov_rx_cb_t prov_rx;
+	void *prov_data;
+	uint32_t prov_timeout;
+	uint16_t algorithms;
 	uint16_t req_index;
 	uint8_t max_filters;
 };
 
+struct join_data{
+	struct l_dbus_message *msg;
+	struct mesh_agent *agent;
+	const char *sender;
+	const char *app_path;
+	struct mesh_node *node;
+	uint32_t disc_watch;
+	uint8_t uuid[16];
+};
+
+struct attach_data {
+	uint64_t token;
+	struct l_dbus_message *msg;
+	const char *app;
+};
+
+static struct bt_mesh mesh;
 static struct l_queue *controllers;
-static struct l_queue *mesh_list;
 static struct mgmt *mgmt_mesh;
 static bool initialized;
-static struct bt_mesh *current;
+
+/* We allow only one outstanding Join request */
+static struct join_data *join_pending;
+
+/* Pending Attach requests */
+static struct l_queue *attach_queue;
 
 static bool simple_match(const void *a, const void *b)
 {
 	return a == b;
 }
 
-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);
-}
-
-static void start_io(struct bt_mesh *mesh, uint16_t index)
+static void start_io(uint16_t index)
 {
 	struct mesh_io *io;
 	struct mesh_io_caps caps;
@@ -91,21 +108,70 @@ static void start_io(struct bt_mesh *mesh, uint16_t index)
 	io = mesh_io_new(index, MESH_IO_TYPE_GENERIC);
 	if (!io) {
 		l_error("Failed to start mesh io (hci %u)", index);
-		current = NULL;
 		return;
 	}
 
 	mesh_io_get_caps(io, &caps);
-	mesh->max_filters = caps.max_num_filters;
+	mesh.max_filters = caps.max_num_filters;
+
+	mesh.io = io;
+
+	l_debug("Started mesh (io %p) on hci %u", mesh.io, index);
+
+	node_attach_io(io);
+}
+
+/* Used for any outbound traffic that doesn't have Friendship Constraints */
+/* This includes Beacons, Provisioning and unrestricted Network Traffic */
+bool mesh_send_pkt(uint8_t count, uint16_t interval,
+					uint8_t *data, uint16_t len)
+{
+	struct mesh_io_send_info info = {
+		.type = MESH_IO_TIMING_TYPE_GENERAL,
+		.u.gen.cnt = count,
+		.u.gen.interval = interval,
+		.u.gen.max_delay = 0,
+		.u.gen.min_delay = 0,
+	};
+
+	return mesh_io_send(mesh.io, &info, data, len);
+}
+
+bool mesh_send_cancel(const uint8_t *filter, uint8_t len)
+{
+	return mesh_io_send_cancel(mesh.io, filter, len);
+}
 
-	mesh_net_attach(mesh->net, io);
-	mesh_net_set_window_accuracy(mesh->net, caps.window_accuracy);
-	mesh->io = io;
-	mesh->index = index;
+static void prov_rx(void *user_data, struct mesh_io_recv_info *info,
+					const uint8_t *data, uint16_t len)
+{
+	if (user_data != &mesh)
+		return;
+
+	if (mesh.prov_rx)
+		mesh.prov_rx(mesh.prov_data, data, len);
+}
+
+bool mesh_reg_prov_rx(prov_rx_cb_t cb, void *user_data)
+{
+	if (mesh.prov_rx && mesh.prov_rx != cb)
+		return false;
 
-	current = NULL;
+	mesh.prov_rx = cb;
+	mesh.prov_data = user_data;
 
-	l_debug("Started mesh (io %p) on hci %u", mesh->io, index);
+	return mesh_io_register_recv_cb(mesh.io, MESH_IO_FILTER_PROV,
+							prov_rx, &mesh);
+}
+
+void mesh_unreg_prov_rx(prov_rx_cb_t cb)
+{
+	if (mesh.prov_rx != cb)
+		return;
+
+	mesh.prov_rx = NULL;
+	mesh.prov_data = NULL;
+	mesh_io_deregister_recv_cb(mesh.io, MESH_IO_FILTER_PROV);
 }
 
 static void read_info_cb(uint8_t status, uint16_t length,
@@ -115,7 +181,7 @@ static void read_info_cb(uint8_t status, uint16_t length,
 	const struct mgmt_rp_read_info *rp = param;
 	uint32_t current_settings, supported_settings;
 
-	if (!current)
+	if (mesh.io)
 		/* Already initialized */
 		return;
 
@@ -148,7 +214,7 @@ static void read_info_cb(uint8_t status, uint16_t length,
 		return;
 	}
 
-	start_io(current, index);
+	start_io(index);
 }
 
 static void index_added(uint16_t index, uint16_t length, const void *param,
@@ -156,11 +222,8 @@ static void index_added(uint16_t index, uint16_t length, const void *param,
 {
 	l_debug("hci device %u", index);
 
-	if (!current)
-		return;
-
-	if (current->req_index != MGMT_INDEX_NONE &&
-					index != current->req_index) {
+	if (mesh.req_index != MGMT_INDEX_NONE &&
+					index != mesh.req_index) {
 		l_debug("Ignore index %d", index);
 		return;
 	}
@@ -219,145 +282,100 @@ static void read_index_list_cb(uint8_t status, uint16_t length,
 	}
 }
 
-static bool load_config(struct bt_mesh *mesh, const char *in_config_name)
+static bool init_mgmt(void)
 {
-	if (!mesh->net)
-		return false;
-
-	if (!storage_parse_config(mesh->net, in_config_name))
+	mgmt_mesh = mgmt_new_default();
+	if (!mgmt_mesh)
 		return false;
 
-	/* Register foundational models */
-	mesh_config_srv_init(mesh->net, PRIMARY_ELE_IDX);
-
-	return true;
-}
-
-static bool init_mesh(void)
-{
-	if (initialized)
-		return true;
-
 	controllers = l_queue_new();
 	if (!controllers)
 		return false;
 
-	mesh_list = l_queue_new();
-	if (!mesh_list)
-		return false;
-
-	mgmt_mesh = mgmt_new_default();
-	if (!mgmt_mesh)
-		goto fail;
-
 	mgmt_register(mgmt_mesh, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
 						index_added, NULL, NULL);
 	mgmt_register(mgmt_mesh, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
 						index_removed, NULL, NULL);
-
-	initialized = true;
 	return true;
-
-fail:
-	l_error("Failed to initialize mesh management");
-
-	l_queue_destroy(controllers, NULL);
-
-	return false;
 }
 
-struct bt_mesh *mesh_new(uint16_t index, const char *config_file)
+bool mesh_init(uint16_t index, const char *config_dir)
 {
-	struct bt_mesh *mesh;
+	if (initialized)
+		return true;
+
+	if (!init_mgmt()) {
+		l_error("Failed to initialize mesh management");
+		return false;
+	}
 
-	if (!init_mesh())
-		return NULL;
+	mesh.req_index = index;
 
-	mesh = l_new(struct bt_mesh, 1);
-	if (!mesh)
-		return NULL;
+	mesh_model_init();
+	mesh_agent_init();
 
-	mesh->req_index = index;
-	mesh->index = MGMT_INDEX_NONE;
+	/* TODO: read mesh.conf */
+	mesh.prov_timeout = DEFAULT_PROV_TIMEOUT;
+	mesh.algorithms = DEFAULT_ALGORITHMS;
 
-	mesh->net = mesh_net_new(index);
-	if (!mesh->net) {
-		l_free(mesh);
-		return NULL;
-	}
+	if (!config_dir)
+		config_dir = MESH_STORAGEDIR;
 
-	if (!load_config(mesh, config_file)) {
-		l_error("Failed to load mesh configuration: %s", config_file);
-		l_free(mesh);
-		return NULL;
-	}
+	l_info("Loading node configuration from %s", config_dir);
+
+	if (!storage_load_nodes(config_dir))
+		return false;
 
-	/*
-	 * TODO: Check if another mesh is searching for io.
-	 * If so, add to pending list and return.
-	 */
 	l_debug("send read index_list");
 	if (mgmt_send(mgmt_mesh, MGMT_OP_READ_INDEX_LIST,
 				MGMT_INDEX_NONE, 0, NULL,
-				read_index_list_cb, mesh, NULL) > 0) {
-		current = mesh;
-		l_queue_push_tail(mesh_list, mesh);
-		return mesh_ref(mesh);
-	}
-
-	l_free(mesh);
+				read_index_list_cb, NULL, NULL) <= 0)
+		return false;
 
-	return NULL;
+	return true;
 }
 
-struct bt_mesh *mesh_ref(struct bt_mesh *mesh)
+static void attach_exit(void *data)
 {
-	if (!mesh)
-		return NULL;
-
-	__sync_fetch_and_add(&mesh->ref_count, 1);
+	struct l_dbus_message *reply;
+	struct attach_data *pending = data;
 
-	return mesh;
+	reply = dbus_error(pending->msg, MESH_ERROR_FAILED, "Failed. Exiting");
+	l_dbus_send(dbus_get_bus(), reply);
+	l_free(pending);
 }
 
-void mesh_unref(struct bt_mesh *mesh)
+void mesh_cleanup(void)
 {
-	struct mesh_io *io;
+	struct l_dbus_message *reply;
 
-	if (!mesh)
-		return;
+	mesh_io_destroy(mesh.io);
+	mgmt_unref(mgmt_mesh);
 
-	if (__sync_sub_and_fetch(&mesh->ref_count, 1))
-		return;
+	if (join_pending) {
+		reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED,
+							"Failed. Exiting");
+		l_dbus_send(dbus_get_bus(), reply);
 
-	if (mesh_net_provisioned_get(mesh->net))
-		save_exit_config(mesh);
+		if (join_pending->disc_watch)
+			l_dbus_remove_watch(dbus_get_bus(),
+						join_pending->disc_watch);
 
-	node_cleanup(mesh->net);
+		if (join_pending->node)
+			node_free(join_pending->node);
 
-	storage_release(mesh->net);
-	io = mesh_net_detach(mesh->net);
-	if (io)
-		mesh_io_destroy(io);
+		l_free(join_pending);
+		join_pending = NULL;
+	}
 
-	mesh_net_unref(mesh->net);
-	l_queue_remove(mesh_list, mesh);
-	l_free(mesh);
-}
+	l_queue_destroy(attach_queue, attach_exit);
+	node_cleanup_all();
+	mesh_model_cleanup();
 
-void mesh_cleanup(void)
-{
 	l_queue_destroy(controllers, NULL);
-	l_queue_destroy(mesh_list, NULL);
-	mgmt_unref(mgmt_mesh);
-}
-
-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);
+	l_dbus_object_remove_interface(dbus_get_bus(), BLUEZ_MESH_PATH,
+							MESH_NETWORK_INTERFACE);
+	l_dbus_unregister_interface(dbus_get_bus(), MESH_NETWORK_INTERFACE);
 }
 
 const char *mesh_status_str(uint8_t err)
@@ -386,7 +404,333 @@ const char *mesh_status_str(uint8_t err)
 	}
 }
 
-struct mesh_net *mesh_get_net(struct bt_mesh *mesh)
+static void free_pending_join_call(bool failed)
+{
+	if (!join_pending)
+		return;
+
+	if (join_pending->disc_watch)
+		l_dbus_remove_watch(dbus_get_bus(),
+						join_pending->disc_watch);
+
+	mesh_agent_remove(join_pending->agent);
+
+	if (failed) {
+		storage_remove_node_config(join_pending->node);
+		mesh_agent_remove(join_pending->agent);
+	}
+
+	l_free(join_pending);
+	join_pending = NULL;
+}
+
+/* This is being called if the app exits unexpectedly */
+static void prov_disc_cb(struct l_dbus *bus, void *user_data)
+{
+	if (!join_pending)
+		return;
+
+	if (join_pending->msg)
+		l_dbus_message_unref(join_pending->msg);
+
+	acceptor_cancel(&mesh);
+
+	join_pending->disc_watch = 0;
+
+	free_pending_join_call(true);
+}
+
+static const char *prov_status_str(uint8_t status)
+{
+	switch (status) {
+	case PROV_ERR_SUCCESS:
+		return "success";
+	case PROV_ERR_INVALID_PDU:
+	case PROV_ERR_INVALID_FORMAT:
+	case PROV_ERR_UNEXPECTED_PDU:
+		return "bad-pdu";
+	case PROV_ERR_CONFIRM_FAILED:
+		return "confirmation-failed";
+	case PROV_ERR_INSUF_RESOURCE:
+		return "out-of-resources";
+	case PROV_ERR_DECRYPT_FAILED:
+		return "decryption-error";
+	case PROV_ERR_CANT_ASSIGN_ADDR:
+		return "cannot-assign-addresses";
+	case PROV_ERR_TIMEOUT:
+		return "timeout";
+	case PROV_ERR_UNEXPECTED_ERR:
+	default:
+		return "unexpected-error";
+	}
+}
+
+static void send_join_failed(const char *owner, const char *path,
+							uint8_t status)
+{
+	struct l_dbus_message *msg;
+	struct l_dbus *dbus = dbus_get_bus();
+
+	msg = l_dbus_message_new_method_call(dbus, owner, path,
+						MESH_APPLICATION_INTERFACE,
+						"JoinFailed");
+
+	l_dbus_message_set_arguments(msg, "s", prov_status_str(status));
+	l_dbus_send(dbus_get_bus(), msg);
+
+	free_pending_join_call(true);
+}
+
+static bool prov_complete_cb(void *user_data, uint8_t status,
+					struct mesh_prov_node_info *info)
+{
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+	const char *owner;
+	const char *path;
+	const uint8_t *dev_key;
+
+	l_debug("Provisioning complete %s", prov_status_str(status));
+
+	if (!join_pending)
+		return false;
+
+	owner = join_pending->sender;
+	path = join_pending->app_path;
+
+	if (status == PROV_ERR_SUCCESS &&
+	    !node_add_pending_local(join_pending->node, info, mesh.io))
+		status = PROV_ERR_UNEXPECTED_ERR;
+
+	if (status != PROV_ERR_SUCCESS) {
+		send_join_failed(owner, path, status);
+		return false;
+	}
+
+	dev_key = node_get_device_key(join_pending->node);
+
+	msg = l_dbus_message_new_method_call(dbus, owner, path,
+						MESH_APPLICATION_INTERFACE,
+						"JoinComplete");
+
+	l_dbus_message_set_arguments(msg, "t", l_get_u64(dev_key));
+
+	l_dbus_send(dbus_get_bus(), msg);
+
+	free_pending_join_call(false);
+
+	return true;
+}
+
+static void node_init_cb(struct mesh_node *node, struct mesh_agent *agent)
+{
+	struct l_dbus_message *reply;
+	uint8_t num_ele;
+
+	if (!node) {
+		reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED,
+				"Failed to create node from application");
+		goto fail;
+	}
+
+	join_pending->node = node;
+	num_ele = node_get_num_elements(node);
+
+	if (!acceptor_start(num_ele, join_pending->uuid, mesh.algorithms,
+				mesh.prov_timeout, agent, prov_complete_cb,
+				&mesh))
+	{
+		reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED,
+				"Failed to start provisioning acceptor");
+		goto fail;
+	}
+
+	reply = l_dbus_message_new_method_return(join_pending->msg);
+	l_dbus_send(dbus_get_bus(), reply);
+	join_pending->msg = NULL;
+
+	return;
+
+fail:
+	l_dbus_send(dbus_get_bus(), reply);
+	mesh_agent_remove(join_pending->agent);
+	l_free(join_pending);
+	join_pending = NULL;
+}
+
+static struct l_dbus_message *join_network_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
 {
-	return mesh->net;
+	const char *app_path, *sender;
+	struct l_dbus_message_iter iter_uuid;
+	uint32_t n;
+
+	l_debug("Join network request");
+
+	if (join_pending)
+		return dbus_error(msg, MESH_ERROR_BUSY,
+						"Provisioning in progress");
+
+	if (!l_dbus_message_get_arguments(msg, "oay", &app_path,
+								&iter_uuid))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	join_pending = l_new(struct join_data, 1);
+
+	l_dbus_message_iter_get_fixed_array(&iter_uuid, join_pending->uuid, &n);
+
+	if (n != 16) {
+		l_free(join_pending);
+		join_pending = NULL;
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+							"Bad device UUID");
+	}
+
+	sender = l_dbus_message_get_sender(msg);
+
+	join_pending->sender = l_strdup(sender);
+	join_pending->disc_watch = l_dbus_add_disconnect_watch(dbus, sender,
+						prov_disc_cb, NULL, NULL);
+	join_pending->msg = l_dbus_message_ref(msg);
+	join_pending->app_path = app_path;
+
+	/* Try to create a temporary node */
+	node_join(app_path, sender, join_pending->uuid, node_init_cb);
+
+	return NULL;
+}
+
+static struct l_dbus_message *cancel_join_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct l_dbus_message *reply;
+
+	l_debug("Cancel Join");
+
+	if (!join_pending) {
+		reply = dbus_error(msg, MESH_ERROR_DOES_NOT_EXIST,
+							"No join in progress");
+		goto done;
+	}
+
+	acceptor_cancel(&mesh);
+
+	/* Return error to the original Join call */
+	if (join_pending->msg) {
+		reply = dbus_error(join_pending->msg, MESH_ERROR_FAILED, NULL);
+		l_dbus_send(dbus_get_bus(), reply);
+	}
+
+	reply = l_dbus_message_new_method_return(msg);
+	l_dbus_message_set_arguments(reply, "");
+
+	free_pending_join_call(true);
+done:
+	return reply;
+}
+
+static bool match_attach_request(const void *a, const void *b)
+{
+	const struct attach_data *pending = a;
+	const uint64_t *token = b;
+
+	return *token == pending->token;
+}
+
+static void attach_ready_cb(int status, char *node_path, uint64_t token)
+{
+	struct l_dbus_message *reply;
+	struct attach_data *pending;
+
+	pending = l_queue_find(attach_queue, match_attach_request, &token);
+	if (!pending)
+		return;
+
+	if (status != MESH_ERROR_NONE) {
+		const char *desc = (status == MESH_ERROR_NOT_FOUND) ?
+				"Node match not found" : "Attach failed";
+		reply = dbus_error(pending->msg, status, desc);
+		goto done;
+	}
+
+	reply = l_dbus_message_new_method_return(pending->msg);
+
+	node_build_attach_reply(reply, token);
+
+done:
+	l_dbus_send(dbus_get_bus(), reply);
+	l_queue_remove(attach_queue, pending);
+	l_free(pending);
+}
+
+static struct l_dbus_message *attach_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	uint64_t token = 1;
+	const char *app_path, *sender;
+	struct attach_data *pending;
+
+	l_debug("Attach");
+
+	if (!l_dbus_message_get_arguments(msg, "ot", &app_path, &token))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	sender = l_dbus_message_get_sender(msg);
+
+	if (node_attach(app_path, sender, token, attach_ready_cb) !=
+								MESH_ERROR_NONE)
+		return dbus_error(msg, MESH_ERROR_NOT_FOUND,
+						"Matching node not found");
+
+	pending = l_new(struct attach_data, 1);
+
+	pending->token = token;
+	pending->msg = l_dbus_message_ref(msg);
+
+	if (!attach_queue)
+		attach_queue = l_queue_new();
+
+	l_queue_push_tail(attach_queue, pending);
+
+	return NULL;
+}
+
+static void setup_network_interface(struct l_dbus_interface *iface)
+{
+	l_dbus_interface_method(iface, "Join", 0, join_network_call, "",
+				"oay", "app", "uuid");
+
+	l_dbus_interface_method(iface, "Cancel", 0, cancel_join_call, "", "");
+
+	l_dbus_interface_method(iface, "Attach", 0, attach_call,
+				"oa(ya(qa{sv}))", "ot", "node", "configuration",
+				"app", "token");
+
+	/* TODO: Implement Leave method */
+}
+
+bool mesh_dbus_init(struct l_dbus *dbus)
+{
+	if (!l_dbus_register_interface(dbus, MESH_NETWORK_INTERFACE,
+						setup_network_interface,
+						NULL, false)) {
+		l_info("Unable to register %s interface",
+			       MESH_NETWORK_INTERFACE);
+		return false;
+	}
+
+	if (!l_dbus_object_add_interface(dbus, BLUEZ_MESH_PATH,
+						MESH_NETWORK_INTERFACE, NULL)) {
+		l_info("Unable to register the mesh object on '%s'",
+							MESH_NETWORK_INTERFACE);
+		l_dbus_unregister_interface(dbus, MESH_NETWORK_INTERFACE);
+		return false;
+	}
+
+	l_info("Added Network Interface on %s", BLUEZ_MESH_PATH);
+
+	return true;
 }
diff --git a/mesh/mesh.h b/mesh/mesh.h
index 8d389883b..ff4e04fa1 100644
--- a/mesh/mesh.h
+++ b/mesh/mesh.h
@@ -15,18 +15,26 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
-struct bt_mesh;
-struct mesh_net;
+#define BLUEZ_MESH_NAME "org.bluez.mesh"
+
+#define MESH_NETWORK_INTERFACE "org.bluez.mesh.Network1"
+#define MESH_NODE_INTERFACE "org.bluez.mesh.Node1"
+#define MESH_ELEMENT_INTERFACE "org.bluez.mesh.Element1"
+#define MESH_APPLICATION_INTERFACE "org.bluez.mesh.Application1"
+#define MESH_PROVISION_AGENT_INTERFACE "org.bluez.mesh.ProvisionAgent1"
+#define ERROR_INTERFACE "org.bluez.mesh.Error"
 
-struct bt_mesh *mesh_new(uint16_t index, const char *in_config_name);
-struct bt_mesh *mesh_ref(struct bt_mesh *mesh);
-void mesh_unref(struct bt_mesh *mesh);
-bool mesh_set_output(struct bt_mesh *mesh, const char *out_config_name);
+typedef void (*prov_rx_cb_t)(void *user_data, const uint8_t *data,
+								uint16_t len);
+bool mesh_init(uint16_t index, const char *in_config_name);
 void mesh_cleanup(void);
-const char *mesh_status_str(uint8_t err);
+bool mesh_dbus_init(struct l_dbus *dbus);
 
-/* Command line testing */
-struct mesh_net *mesh_get_net(struct bt_mesh *mesh);
+const char *mesh_status_str(uint8_t err);
+bool mesh_send_pkt(uint8_t count, uint16_t interval, uint8_t *data,
+								uint16_t len);
+bool mesh_send_cancel(const uint8_t *filter, uint8_t len);
+bool mesh_reg_prov_rx(prov_rx_cb_t cb, void *user_data);
+void mesh_unreg_prov_rx(prov_rx_cb_t cb);
-- 
2.14.5


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

* [PATCH BlueZ v6 15/26] mesh: Multi node Config Server model
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (13 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 14/26] mesh: Re-architect " Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 16/26] mesh: restructure I/O for multiple nodes Brian Gix
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

The Config Server model is an internal service that allows
priviledged remote Config Client nodes to manipulate and
configure local keys, subscription lists, and publications of
models on specific local nodes. It has been rewritten to allow
control of multiple nodes on the local device.
---
 mesh/cfgmod-server.c | 179 ++++++++++++++++++++++++++-------------------------
 mesh/cfgmod.h        |   3 +-
 2 files changed, 93 insertions(+), 89 deletions(-)

diff --git a/mesh/cfgmod-server.c b/mesh/cfgmod-server.c
index 2c4a02912..062bdaaf2 100644
--- a/mesh/cfgmod-server.c
+++ b/mesh/cfgmod-server.c
@@ -15,7 +15,6 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -24,11 +23,14 @@
 
 #include <unistd.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <sys/time.h>
 #include <ell/ell.h>
+#include <ell/ell.h>
 
-#include "mesh/mesh-defs.h"
+#include "json-c/json.h"
 
+#include "mesh/mesh-defs.h"
 #include "mesh/mesh.h"
 #include "mesh/node.h"
 #include "mesh/net.h"
@@ -40,7 +42,7 @@
 
 #define CFG_MAX_MSG_LEN 380
 
-static void send_pub_status(struct mesh_net *net, uint16_t src, uint16_t dst,
+static void send_pub_status(struct mesh_node *node, 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)
@@ -60,7 +62,7 @@ static void send_pub_status(struct mesh_net *net, uint16_t src, uint16_t dst,
 	msg[n++] = ttl;
 	msg[n++] = period;
 	msg[n++] = retransmit;
-	if (mod_id < 0x10000) {
+	if (mod_id < 0x10000 || mod_id > VENDOR_ID_MASK) {
 		l_put_le16(mod_id, msg + n);
 		n += 2;
 	} else {
@@ -70,12 +72,11 @@ static void send_pub_status(struct mesh_net *net, uint16_t src, uint16_t dst,
 		n += 2;
 	}
 
-	mesh_model_send(net, CONFIG_SRV_MODEL,
-			dst, src,
+	mesh_model_send(node, dst, src,
 			APP_IDX_DEV, DEFAULT_TTL, msg, n);
 }
 
-static bool config_pub_get(struct mesh_net *net, uint16_t src, uint16_t dst,
+static bool config_pub_get(struct mesh_node *node, uint16_t src, uint16_t dst,
 					const uint8_t *pkt, uint16_t size)
 {
 	uint32_t mod_id;
@@ -84,33 +85,34 @@ static bool config_pub_get(struct mesh_net *net, uint16_t src, uint16_t dst,
 	struct mesh_model_pub *pub = NULL;
 	int status;
 
-	if (size == 4)
+	if (size == 4) {
 		mod_id = l_get_le16(pkt + 2);
-	else if (size == 6) {
+		mod_id |= VENDOR_ID_MASK;
+	} 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);
+	ele_idx = node_get_element_idx(node, ele_addr);
 
 	if (ele_idx >= 0)
-		pub = mesh_model_pub_get(net, ele_idx, mod_id, &status);
+		pub = mesh_model_pub_get(node, 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,
+		send_pub_status(node, 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,
+		send_pub_status(node, 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,
+static bool config_pub_set(struct mesh_node *node, uint16_t src, uint16_t dst,
 					const uint8_t *pkt, uint16_t size,
 					bool unreliable)
 {
@@ -133,6 +135,7 @@ static bool config_pub_set(struct mesh_net *net, uint16_t src, uint16_t dst,
 		period = pkt[7];
 		retransmit = pkt[8];
 		mod_id = l_get_le16(pkt + 9);
+		mod_id |= VENDOR_ID_MASK;
 		break;
 
 	case 13:
@@ -151,6 +154,7 @@ static bool config_pub_set(struct mesh_net *net, uint16_t src, uint16_t dst,
 		period = pkt[21];
 		retransmit = pkt[22];
 		mod_id = l_get_le16(pkt + 23);
+		mod_id |= VENDOR_ID_MASK;
 		break;
 
 	case 27:
@@ -181,24 +185,24 @@ static bool config_pub_set(struct mesh_net *net, uint16_t src, uint16_t dst,
 	if (!b_virt && test_addr > 0x7fff && test_addr < 0xc000)
 		return false;
 
-	status = mesh_model_pub_set(net, ele_addr, mod_id, pub_addr, idx,
+	status = mesh_model_pub_set(node, 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",
+	l_debug("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,
+		send_pub_status(node, 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,
+static void send_sub_status(struct mesh_node *node, uint16_t src, uint16_t dst,
 					uint8_t status, uint16_t ele_addr,
 					uint16_t addr, uint32_t mod)
 {
@@ -210,7 +214,7 @@ static void send_sub_status(struct mesh_net *net, uint16_t src, uint16_t dst,
 	n += 2;
 	l_put_le16(addr, msg + n);
 	n += 2;
-	if (mod >= 0x10000) {
+	if (mod >= 0x10000 && mod < VENDOR_ID_MASK) {
 		l_put_le16(mod >> 16, msg + n);
 		l_put_le16(mod, msg + n + 2);
 		n += 4;
@@ -219,11 +223,10 @@ static void send_sub_status(struct mesh_net *net, uint16_t src, uint16_t dst,
 		n += 2;
 	}
 
-	mesh_model_send(net, CONFIG_SRV_MODEL, dst, src, APP_IDX_DEV,
-							DEFAULT_TTL, msg, n);
+	mesh_model_send(node, dst, src, APP_IDX_DEV, DEFAULT_TTL, msg, n);
 }
 
-static bool config_sub_get(struct mesh_net *net, uint16_t src, uint16_t dst,
+static bool config_sub_get(struct mesh_node *node, uint16_t src, uint16_t dst,
 					const uint8_t *pkt, uint16_t size)
 {
 	uint16_t ele_addr;
@@ -251,6 +254,7 @@ static bool config_sub_get(struct mesh_net *net, uint16_t src, uint16_t dst,
 		n += 2;
 		l_put_le16(mod_id, msg + n);
 		n += 2;
+		mod_id |= VENDOR_ID_MASK;
 		break;
 
 	case 6:
@@ -269,7 +273,7 @@ static bool config_sub_get(struct mesh_net *net, uint16_t src, uint16_t dst,
 	}
 
 	buf_size = sizeof(uint16_t) * MAX_GRP_PER_MOD;
-	ret = mesh_model_sub_get(net, ele_addr, mod_id, msg + n, buf_size,
+	ret = mesh_model_sub_get(node, ele_addr, mod_id, msg + n, buf_size,
 									&size);
 
 	if (!ret)
@@ -277,13 +281,11 @@ static bool config_sub_get(struct mesh_net *net, uint16_t src, uint16_t dst,
 	else if (ret > 0)
 		*status = ret;
 
-	mesh_model_send(net, CONFIG_SRV_MODEL,
-			dst, src, APP_IDX_DEV,
-			DEFAULT_TTL, msg, n);
+	mesh_model_send(node, 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,
+static void config_sub_set(struct mesh_node *node, uint16_t src, uint16_t dst,
 					const uint8_t *pkt, uint16_t size,
 					bool virt, uint32_t opcode)
 {
@@ -301,13 +303,15 @@ static void config_sub_set(struct mesh_net *net, uint16_t src, uint16_t dst,
 		if (opcode != OP_CONFIG_MODEL_SUB_DELETE_ALL)
 			return;
 		mod_id = l_get_le16(pkt + 2);
+		mod_id |= VENDOR_ID_MASK;
 		break;
 	case 6:
 		if (virt)
 			return;
-		if (opcode != OP_CONFIG_MODEL_SUB_DELETE_ALL)
+		if (opcode != OP_CONFIG_MODEL_SUB_DELETE_ALL) {
 			mod_id = l_get_le16(pkt + 4);
-		else {
+			mod_id |= VENDOR_ID_MASK;
+		} else {
 			mod_id = l_get_le16(pkt + 2) << 16;
 			mod_id |= l_get_le16(pkt + 4);
 		}
@@ -322,6 +326,7 @@ static void config_sub_set(struct mesh_net *net, uint16_t src, uint16_t dst,
 		if (!virt)
 			return;
 		mod_id = l_get_le16(pkt + 18);
+		mod_id |= VENDOR_ID_MASK;
 		break;
 	case 22:
 		if (!virt)
@@ -341,42 +346,42 @@ static void config_sub_set(struct mesh_net *net, uint16_t src, uint16_t dst,
 	func = opcode & ~OP_UNRELIABLE;
 	switch (func) {
 	default:
-		l_info("Bad opcode: %x", func);
+		l_debug("Bad opcode: %x", func);
 		return;
 
 	case OP_CONFIG_MODEL_SUB_DELETE_ALL:
-		status = mesh_model_sub_del_all(net, ele_addr, mod_id);
+		status = mesh_model_sub_del_all(node, 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,
+		status = mesh_model_sub_ovr(node, 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,
+		status = mesh_model_sub_add(node, 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,
+		status = mesh_model_sub_del(node, ele_addr, mod_id,
 							addr, virt, &grp);
 		break;
 	}
 
 	if (!unreliable && status >= 0)
-		send_sub_status(net, src, dst, status, ele_addr, grp, mod_id);
+		send_sub_status(node, src, dst, status, ele_addr, grp, mod_id);
 
 }
 
-static void send_model_app_status(struct mesh_net *net, uint16_t src,
+static void send_model_app_status(struct mesh_node *node, uint16_t src,
 					uint16_t dst, uint8_t status,
 					uint16_t addr, uint32_t id,
 					uint16_t idx)
@@ -389,18 +394,17 @@ static void send_model_app_status(struct mesh_net *net, uint16_t src,
 	n += 2;
 	l_put_le16(idx, msg + n);
 	n += 2;
-	if (id > 0xffff) {
+	if (id >= 0x10000 && id < VENDOR_ID_MASK) {
 		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);
+	mesh_model_send(node, dst, src, APP_IDX_DEV, DEFAULT_TTL, msg, n);
 }
 
-static void model_app_list(struct mesh_net *net, uint16_t src, uint16_t dst,
+static void model_app_list(struct mesh_node *node, uint16_t src, uint16_t dst,
 					const uint8_t *pkt, uint16_t size)
 {
 	uint16_t ele_addr;
@@ -427,6 +431,7 @@ static void model_app_list(struct mesh_net *net, uint16_t src, uint16_t dst,
 		mod_id = l_get_le16(pkt + 2);
 		l_put_le16(ele_addr, msg + 1 + n);
 		l_put_le16(mod_id, msg + 3 + n);
+		mod_id |= VENDOR_ID_MASK;
 		n += 5;
 		break;
 	case 6:
@@ -443,20 +448,20 @@ static void model_app_list(struct mesh_net *net, uint16_t src, uint16_t dst,
 	}
 
 
-	result = mesh_model_get_bindings(net, ele_addr, mod_id, msg + n,
+	result = mesh_model_get_bindings(node, 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);
+		mesh_model_send(node, 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,
+static bool model_app_bind(struct mesh_node *node, uint16_t src, uint16_t dst,
 					const uint8_t *pkt, uint16_t size,
 					bool unbind)
 {
@@ -471,6 +476,7 @@ static bool model_app_bind(struct mesh_net *net, uint16_t src, uint16_t dst,
 
 	case 6:
 		mod_id = l_get_le16(pkt + 4);
+		mod_id |= VENDOR_ID_MASK;
 		break;
 	case 8:
 		mod_id = l_get_le16(pkt + 4) << 16;
@@ -485,11 +491,11 @@ static bool model_app_bind(struct mesh_net *net, uint16_t src, uint16_t dst,
 		return false;
 
 	if (unbind)
-		result = mesh_model_binding_del(net, ele_addr, mod_id, idx);
+		result = mesh_model_binding_del(node, ele_addr, mod_id, idx);
 	else
-		result = mesh_model_binding_add(net, ele_addr, mod_id, idx);
+		result = mesh_model_binding_add(node, ele_addr, mod_id, idx);
 
-	send_model_app_status(net, src, dst, result, ele_addr, mod_id, idx);
+	send_model_app_status(node, src, dst, result, ele_addr, mod_id, idx);
 
 	return true;
 }
@@ -533,7 +539,7 @@ 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_debug("HB Subscription Ended");
 	l_timeout_remove(hb->sub_timer);
 	hb->sub_timer = NULL;
 	hb->sub_enabled = false;
@@ -639,7 +645,7 @@ static int hb_subscription_set(struct mesh_net *net, uint16_t src,
 
 static void node_reset(struct l_timeout *timeout, void *user_data)
 {
-	l_info("Node Reset");
+	l_debug("Node Reset");
 	l_timeout_remove(timeout);
 	l_main_quit();
 }
@@ -649,7 +655,8 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 				const uint8_t *data, uint16_t size,
 				uint8_t ttl, const void *user_data)
 {
-	struct mesh_net *net = (struct mesh_net *) user_data;
+	struct mesh_node *node = (struct mesh_node *) user_data;
+	struct mesh_net *net = node_get_net(node);
 	const uint8_t *pkt = data;
 	struct timeval time_now;
 	uint32_t opcode, tmp32;
@@ -663,7 +670,6 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 	bool virt = false;
 	uint8_t count;
 	uint16_t interval;
-	struct mesh_node *node;
 	uint16_t n;
 
 	if (idx != APP_IDX_DEV)
@@ -675,10 +681,10 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 	} else
 		return false;
 
+	net = node_get_net(node);
 	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) {
@@ -691,8 +697,8 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 
 		/* Only page 0 is currently supported */
 		if (pkt[0] != 0) {
-			l_info("Unsupported page number %d", pkt[0]);
-			l_info("Returning page number 0");
+			l_debug("Unsupported page number %d", pkt[0]);
+			l_debug("Returning page number 0");
 		}
 		long_msg = l_malloc(CFG_MAX_MSG_LEN);
 		n = mesh_model_opcode_set(OP_DEV_COMP_STATUS, long_msg);
@@ -711,7 +717,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		/* Fall Through */
 
 	case OP_CONFIG_DEFAULT_TTL_GET:
-		l_info("Get/Set Default TTL");
+		l_debug("Get/Set Default TTL");
 
 		n = mesh_model_opcode_set(OP_CONFIG_DEFAULT_TTL_STATUS, msg);
 		msg[n++] = node_default_ttl_get(node);
@@ -721,7 +727,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		if (size != 25 && size != 27)
 			return true;
 
-		config_pub_set(net, src, unicast, pkt, size,
+		config_pub_set(node, src, unicast, pkt, size,
 				!!(opcode & OP_UNRELIABLE));
 		break;
 
@@ -729,24 +735,24 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		if (size != 11 && size != 13)
 			return true;
 
-		config_pub_set(net, src, unicast, pkt, size,
+		config_pub_set(node, src, unicast, pkt, size,
 				!!(opcode & OP_UNRELIABLE));
 		break;
 
 	case OP_CONFIG_MODEL_PUB_GET:
-		config_pub_get(net, src, unicast, pkt, size);
+		config_pub_get(node, 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);
+		config_sub_get(node, src, unicast, pkt, size);
 		break;
 
 	case OP_CONFIG_MODEL_SUB_GET:
 		if (size != 4)
 			return true;
-		config_sub_get(net, src, unicast, pkt, size);
+		config_sub_get(node, src, unicast, pkt, size);
 		break;
 
 	case OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE:
@@ -758,7 +764,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 	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);
+		config_sub_set(node, src, unicast, pkt, size, virt, opcode);
 		break;
 
 	case OP_CONFIG_RELAY_SET:
@@ -777,7 +783,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		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]);
+		l_debug("Get/Set Relay Config (%d)", msg[n-1]);
 		break;
 
 	case OP_CONFIG_NETWORK_TRANSMIT_SET:
@@ -786,7 +792,8 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 
 		count = (pkt[0] >> 5) + 1;
 		interval = ((pkt[0] & 0x1f) + 1) * 10;
-		if (storage_local_set_transmit_params(net, count, interval))
+		if (storage_set_transmit_params(node_jconfig_get(node), count,
+								interval))
 			mesh_net_transmit_params_set(net, count, interval);
 		/* Fall Through */
 
@@ -796,7 +803,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		mesh_net_transmit_params_get(net, &count, &interval);
 		msg[n++] = ((count - 1) << 5) + ((interval/10 - 1) & 0x1f);
 
-		l_info("Get/Set Network Transmit Config");
+		l_debug("Get/Set Network Transmit Config");
 		break;
 
 	case OP_CONFIG_PROXY_SET:
@@ -810,7 +817,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		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]);
+		l_debug("Get/Set Config Proxy (%d)", msg[n-1]);
 		break;
 
 	case OP_NODE_IDENTITY_SET:
@@ -845,7 +852,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		n += 2;
 
 		msg[n++] = state;
-		l_info("Get/Set Config Identity (%d)", state);
+		l_debug("Get/Set Config Identity (%d)", state);
 		break;
 
 	case OP_CONFIG_BEACON_SET:
@@ -859,7 +866,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		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]);
+		l_debug("Get/Set Config Beacon (%d)", msg[n-1]);
 		break;
 
 	case OP_CONFIG_FRIEND_SET:
@@ -874,7 +881,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		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]);
+		l_debug("Get/Set Friend (%d)", msg[n-1]);
 		break;
 
 	case OP_CONFIG_KEY_REFRESH_PHASE_SET:
@@ -908,7 +915,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		n += 2;
 		msg[n++] = phase;
 
-		l_info("Get/Set Key Refresh State (%d)", msg[n-1]);
+		l_debug("Get/Set Key Refresh State (%d)", msg[n-1]);
 		break;
 
 	case OP_APPKEY_ADD:
@@ -921,7 +928,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		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",
+		l_debug("Add/Update AppKey %s: Net_Idx %3.3x, App_Idx %3.3x",
 			(b_res == MESH_STATUS_SUCCESS) ? "success" : "fail",
 							net_idx, app_idx);
 
@@ -942,9 +949,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		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",
+		l_debug("Delete AppKey %s Net_Idx %3.3x to App_Idx %3.3x",
 			(b_res == MESH_STATUS_SUCCESS) ? "success" : "fail",
 							net_idx, app_idx);
 
@@ -979,7 +984,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		b_res = mesh_net_add_key(net, opcode == OP_NETKEY_UPDATE,
 						l_get_le16(pkt), pkt + 2);
 
-		l_info("NetKey Add/Update %s",
+		l_debug("NetKey Add/Update %s",
 			(b_res == MESH_STATUS_SUCCESS) ? "success" : "fail");
 
 		n = mesh_model_opcode_set(OP_NETKEY_STATUS, msg);
@@ -994,7 +999,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 
 		b_res = mesh_net_del_key(net, l_get_le16(pkt));
 
-		l_info("NetKey delete %s",
+		l_debug("NetKey delete %s",
 			(b_res == MESH_STATUS_SUCCESS) ? "success" : "fail");
 
 		n = mesh_model_opcode_set(OP_NETKEY_STATUS, msg);
@@ -1016,26 +1021,26 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 
 	case OP_MODEL_APP_BIND:
 	case OP_MODEL_APP_UNBIND:
-		model_app_bind(net, src, unicast, pkt, size,
+		model_app_bind(node, 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);
+		model_app_list(node, src, unicast, pkt, size);
 		break;
 
 	case OP_MODEL_APP_GET:
 		if (size != 4)
 			return true;
-		model_app_list(net, src, unicast, pkt, size);
+		model_app_list(node, src, unicast, pkt, size);
 		break;
 
 	case OP_CONFIG_HEARTBEAT_PUB_SET:
-		l_info("OP_CONFIG_HEARTBEAT_PUB_SET");
+		l_debug("OP_CONFIG_HEARTBEAT_PUB_SET");
 		if (size != 9) {
-			l_info("bad size %d", size);
+			l_debug("bad size %d", size);
 			return true;
 		}
 		if (pkt[2] > 0x11 || pkt[3] > 0x10 || pkt[4] > 0x7f)
@@ -1101,7 +1106,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		if (size != 5)
 			return true;
 
-		l_info("Set Sub Period (Log %2.2x) %d sec",
+		l_debug("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),
@@ -1121,7 +1126,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 		else
 			time_now.tv_sec = hb->sub_period - time_now.tv_sec;
 
-		l_info("Sub Period (Log %2.2x) %d sec",
+		l_debug("Sub Period (Log %2.2x) %d sec",
 				uint32_to_log(time_now.tv_sec),
 				(int) time_now.tv_sec);
 
@@ -1159,8 +1164,7 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 
 	if (n) {
 		/* print_packet("App Tx", long_msg ? long_msg : msg, n); */
-		mesh_model_send(net, CONFIG_SRV_MODEL,
-				unicast, src,
+		mesh_model_send(node, unicast, src,
 				APP_IDX_DEV, DEFAULT_TTL,
 				long_msg ? long_msg : msg, n);
 	}
@@ -1171,7 +1175,8 @@ static bool cfg_srv_pkt(uint16_t src, uint32_t dst,
 
 static void cfgmod_srv_unregister(void *user_data)
 {
-	struct mesh_net *net = user_data;
+	struct mesh_node *node = user_data;
+	struct mesh_net *net = node_get_net(node);
 	struct mesh_net_heartbeat *hb = mesh_net_heartbeat_get(net);
 
 	l_timeout_remove(hb->pub_timer);
@@ -1187,8 +1192,8 @@ static const struct mesh_model_ops ops = {
 	.pub = NULL
 };
 
-void mesh_config_srv_init(struct mesh_net *net, uint8_t ele_idx)
+void mesh_config_srv_init(struct mesh_node *node, uint8_t ele_idx)
 {
 	l_debug("%2.2x", ele_idx);
-	mesh_model_register(net, ele_idx, CONFIG_SRV_MODEL, &ops, net);
+	mesh_model_register(node, ele_idx, CONFIG_SRV_MODEL, &ops, node);
 }
diff --git a/mesh/cfgmod.h b/mesh/cfgmod.h
index bedb0c6f6..84fa184e5 100644
--- a/mesh/cfgmod.h
+++ b/mesh/cfgmod.h
@@ -15,7 +15,6 @@
  *  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)
@@ -95,4 +94,4 @@
 #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);
+void mesh_config_srv_init(struct mesh_node *node, uint8_t ele_idx);
-- 
2.14.5


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

* [PATCH BlueZ v6 16/26] mesh: restructure I/O for multiple nodes
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (14 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 15/26] mesh: Multi node Config Server model Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 17/26] mesh: Restructure DB to support " Brian Gix
                   ` (9 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

Restructured access to Bluetooth Controllers to allow the
usage of Multiple controllers, and potentially functionality
tailored to Mesh specific features.
---
 mesh/mesh-io.c | 3 ++-
 mesh/mesh-io.h | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/mesh/mesh-io.c b/mesh/mesh-io.c
index 5cdfdc5ff..8cf6c486a 100644
--- a/mesh/mesh-io.c
+++ b/mesh/mesh-io.c
@@ -177,7 +177,8 @@ bool mesh_io_send(struct mesh_io *io, struct mesh_io_send_info *info,
 	return false;
 }
 
-bool mesh_io_send_cancel(struct mesh_io *io, uint8_t *pattern, uint8_t len)
+bool mesh_io_send_cancel(struct mesh_io *io, const uint8_t *pattern,
+								uint8_t len)
 {
 	io = l_queue_find(io_list, match_by_io, io);
 
diff --git a/mesh/mesh-io.h b/mesh/mesh-io.h
index 754f6129c..71d3cb980 100644
--- a/mesh/mesh-io.h
+++ b/mesh/mesh-io.h
@@ -96,4 +96,5 @@ bool mesh_set_filter(struct mesh_io *io, uint8_t filter_id,
 
 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);
+bool mesh_io_send_cancel(struct mesh_io *io, const uint8_t *pattern,
+								uint8_t len);
-- 
2.14.5


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

* [PATCH BlueZ v6 17/26] mesh: Restructure DB to support multiple nodes
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (15 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 16/26] mesh: restructure I/O for multiple nodes Brian Gix
@ 2018-12-28 22:07 ` " Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 18/26] mesh: Restructure model services for " Brian Gix
                   ` (8 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

Added needed functionality to support the storage of multiple
subnets, subscription lists, and publications per node.
---
 mesh/mesh-db.c | 456 +++++++++++++++++++++++++++++++++++++--------------------
 mesh/mesh-db.h |   9 +-
 2 files changed, 297 insertions(+), 168 deletions(-)

diff --git a/mesh/mesh-db.c b/mesh/mesh-db.c
index 86e17ed9a..5c0b72551 100644
--- a/mesh/mesh-db.c
+++ b/mesh/mesh-db.c
@@ -15,7 +15,6 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -29,6 +28,7 @@
 #include <string.h>
 
 #include <ell/ell.h>
+#include <json-c/json.h>
 
 #include "mesh/mesh-defs.h"
 #include "mesh/util.h"
@@ -162,6 +162,7 @@ static json_object *jarray_string_del(json_object *jarray, char *str,
 		if (str_entry && !strncmp(str, str_entry, len))
 			continue;
 
+		json_object_get(jentry);
 		json_object_array_add(jarray_new, jentry);
 	}
 
@@ -193,9 +194,6 @@ 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)
@@ -203,16 +201,17 @@ static json_object *jarray_key_del(json_object *jarray, int16_t idx)
 
 	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))
+			int tmp = json_object_get_int(jvalue);
+
+			if (tmp == idx)
 				continue;
 		}
 
+		json_object_get(jentry);
 		json_object_array_add(jarray_new, jentry);
 	}
 
@@ -389,19 +388,10 @@ bool mesh_db_net_key_add(json_object *jobj, uint16_t idx,
 	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;
@@ -422,7 +412,7 @@ bool mesh_db_net_key_add(json_object *jobj, uint16_t idx,
 		return false;
 	}
 
-	if (phase == KEY_REFRESH_PHASE_NONE) {
+	if (!jentry) {
 		jentry = json_object_new_object();
 		if (!jentry)
 			goto fail;
@@ -442,6 +432,19 @@ bool mesh_db_net_key_add(json_object *jobj, uint16_t idx,
 		if (!add_key(jentry, "key", key))
 			goto fail;
 
+		/* If Key Refresh underway, add placeholder for "Old Key" */
+		if (phase != KEY_REFRESH_PHASE_NONE) {
+			uint8_t buf[16];
+			uint8_t i;
+
+			/* Flip Bits to differentiate */
+			for (i = 0; i < sizeof(buf); i++)
+				buf[i] = key[i] ^ 0xff;
+
+			if (!add_key(jentry, "oldKey", buf))
+				goto fail;
+		}
+
 		if (!jarray) {
 			jarray = json_object_new_array();
 			if (!jarray)
@@ -733,7 +736,7 @@ static bool parse_bindings(json_object *jbindings, struct mesh_db_model *mod)
 	if (cnt > 0xffff)
 		return false;
 
-	mod->num_subs = cnt;
+	mod->num_bindings = cnt;
 
 	/* Allow empty bindings list */
 	if (!cnt)
@@ -761,6 +764,130 @@ static bool parse_bindings(json_object *jbindings, struct mesh_db_model *mod)
 	return true;
 }
 
+static bool get_key_index(json_object *jobj, const char *keyword,
+								uint16_t *index)
+{
+	int idx;
+
+	if (!get_int(jobj, keyword, &idx))
+		return false;
+
+	if (!CHECK_KEY_IDX_RANGE(idx))
+		return false;
+
+	*index = (uint16_t) idx;
+	return true;
+}
+
+static struct mesh_db_pub *parse_model_publication(json_object *jpub)
+{
+	json_object *jvalue;
+	struct mesh_db_pub *pub;
+	int len, value;
+	char *str;
+
+	pub = l_new(struct mesh_db_pub, 1);
+	if (!pub)
+		return NULL;
+
+	json_object_object_get_ex(jpub, "address", &jvalue);
+	str = (char *)json_object_get_string(jvalue);
+	len = strlen(str);
+
+	switch (len) {
+	case 4:
+		if (sscanf(str, "%04hx", &pub->addr) != 1)
+			goto fail;
+		break;
+	case 32:
+		if (!str2hex(str, len, pub->virt_addr, 16))
+			goto fail;
+		pub->virt = true;
+		break;
+	default:
+		goto fail;
+	}
+
+	if (!get_key_index(jpub, "index", &pub->idx))
+		goto fail;
+
+	if (!get_int(jpub, "ttl", &value))
+		goto fail;
+	pub->ttl = (uint8_t) value;
+
+	if (!get_int(jpub, "period", &value))
+		goto fail;
+	pub->period = (uint8_t) value;
+
+	if (!get_int(jpub, "credentials", &value))
+		goto fail;
+	pub->credential = (uint8_t) value;
+
+	if (!get_int(jpub, "retransmit", &value))
+		goto fail;
+
+	pub->retransmit = (uint8_t) value;
+	return pub;
+
+fail:
+	l_free(pub);
+	return NULL;
+}
+
+static bool parse_model_subscriptions(json_object *jsubs,
+						struct mesh_db_model *mod)
+{
+	struct mesh_db_sub *subs;
+	int i, cnt;
+
+	if (json_object_get_type(jsubs) != json_type_array)
+		return NULL;
+
+	cnt = json_object_array_length(jsubs);
+	/* Allow empty array */
+	if (!cnt)
+		return true;
+
+	subs = l_new(struct mesh_db_sub, cnt);
+	if (!subs)
+		return false;
+
+	for (i = 0; i < cnt; ++i) {
+		char *str;
+		int len;
+		json_object *jvalue;
+
+		jvalue = json_object_array_get_idx(jsubs, i);
+		if (!jvalue)
+			return false;
+
+		str = (char *)json_object_get_string(jvalue);
+		len = strlen(str);
+
+		switch (len) {
+		case 4:
+			if (sscanf(str, "%04hx", &subs[i].src.addr) != 1)
+				goto fail;
+		break;
+		case 32:
+			if (!str2hex(str, len, subs[i].src.virt_addr, 16))
+				goto fail;
+			subs[i].virt = true;
+			break;
+		default:
+			goto fail;
+		}
+	}
+
+	mod->num_subs = cnt;
+	mod->subs = subs;
+
+	return true;
+fail:
+	l_free(subs);
+	return false;
+}
+
 static bool parse_models(json_object *jmodels, struct mesh_db_element *ele)
 {
 	int i, num_models;
@@ -810,11 +937,22 @@ static bool parse_models(json_object *jmodels, struct mesh_db_element *ele)
 
 		json_object_object_get_ex(jmodel, "bind", &jarray);
 
-		if (jarray && (json_object_get_type(jmodels) != json_type_array
+		if (jarray && (json_object_get_type(jarray) != json_type_array
 					|| !parse_bindings(jarray, mod)))
 			goto fail;
 
-		/* TODO add pub/sub */
+		json_object_object_get_ex(jmodel, "publish", &jvalue);
+		if (jvalue) {
+			mod->pub = parse_model_publication(jvalue);
+			if (!mod->pub)
+				goto fail;
+		}
+
+		json_object_object_get_ex(jmodel, "subscribe", &jarray);
+
+		if (jarray && !parse_model_subscriptions(jarray, mod))
+			goto fail;
+
 		l_queue_push_tail(ele->models, mod);
 	}
 
@@ -1012,33 +1150,6 @@ static bool parse_composition(json_object *jcomp, struct mesh_db_node *node)
 	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;
@@ -1091,117 +1202,28 @@ bool mesh_db_read_node(json_object *jnode, mesh_db_node_cb cb, void *user_data)
 	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)
+bool mesh_db_write_uint16_hex(json_object *jobj, const char *desc,
+								uint16_t value)
 {
-	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;
+	json_object *jstring;
+	char buf[5];
 
-	str = (char *)json_object_get_string(jvalue);
-	if (!str2hex(str, strlen(str), prov->priv_key, 32))
+	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_uint16_hex(json_object *jobj, const char *desc,
-								uint16_t value)
+bool mesh_db_write_uint32_hex(json_object *jobj, const char *desc,
+								uint32_t value)
 {
 	json_object *jstring;
-	char buf[5];
+	char buf[9];
 
-	snprintf(buf, 5, "%4.4x", value);
+	snprintf(buf, 9, "%8.8x", value);
 	jstring = json_object_new_string(buf);
 	if (!jstring)
 		return false;
@@ -1238,23 +1260,23 @@ bool mesh_db_write_bool(json_object *jobj, const char *keyword, bool value)
 	return true;
 }
 
-bool mesh_db_write_mode(json_object *jobj, const char *keyword, int value)
+static const char *mode_to_string(int mode)
 {
-	json_object *jstring;
-
-	switch (value) {
+	switch (mode) {
 	case MESH_MODE_DISABLED:
-		jstring = json_object_new_string("disabled");
-		break;
+		return "disabled";
 	case MESH_MODE_ENABLED:
-		jstring = json_object_new_string("enabled");
-		break;
-	case MESH_MODE_UNSUPPORTED:
-		jstring = json_object_new_string("unsupported");
-		break;
+		return "enabled";
 	default:
-		return false;
-	};
+		return "unsupported";
+	}
+}
+
+bool mesh_db_write_mode(json_object *jobj, const char *keyword, int value)
+{
+	json_object *jstring;
+
+	jstring = json_object_new_string(mode_to_string(value));
 
 	if (!jstring)
 		return false;
@@ -1272,7 +1294,7 @@ bool mesh_db_write_relay_mode(json_object *jnode, uint8_t mode, uint8_t count,
 	json_object_object_del(jnode, "relay");
 
 	jrelay = json_object_new_object();
-	if (jrelay)
+	if (!jrelay)
 		return false;
 
 	if (!mesh_db_write_mode(jrelay, "mode", mode))
@@ -1359,3 +1381,113 @@ void mesh_db_remove_property(json_object *jobj, const char *desc)
 {
 	json_object_object_del(jobj, desc);
 }
+
+static void add_model(void *a, void *b)
+{
+	struct mesh_db_model *mod = a;
+	json_object *jmodels = b, *jmodel;
+
+	jmodel = json_object_new_object();
+	if (!jmodel)
+		return;
+
+	if (!mod->vendor)
+		mesh_db_write_uint16_hex(jmodel, "modelId",
+						(uint16_t) mod->id);
+	else
+		mesh_db_write_uint32_hex(jmodel, "modelId", mod->id);
+
+	json_object_array_add(jmodels, jmodel);
+}
+
+/* Add unprovisioned node (local) */
+bool mesh_db_add_node(json_object *jnode, struct mesh_db_node *node) {
+
+	struct mesh_db_modes *modes = &node->modes;
+	const struct l_queue_entry *entry;
+	json_object *jelements;
+
+	/* CID, PID, VID, crpl */
+	if (!mesh_db_write_uint16_hex(jnode, "cid", node->cid))
+		return false;
+
+	if (!mesh_db_write_uint16_hex(jnode, "pid", node->pid))
+		return false;
+
+	if (!mesh_db_write_uint16_hex(jnode, "vid", node->vid))
+		return false;
+
+	if (!mesh_db_write_uint16_hex(jnode, "crpl", node->crpl))
+		return false;
+
+	/* Device UUID */
+	if (!add_key(jnode, "UUID", node->uuid))
+		return false;
+
+	/* Features: relay, LPN, friend, proxy*/
+	if (!mesh_db_write_relay_mode(jnode, modes->relay.state,
+						modes->relay.cnt,
+						modes->relay.interval))
+		return false;
+
+	if (!mesh_db_write_mode(jnode, "lowPower", modes->lpn))
+		return false;
+
+	if (!mesh_db_write_mode(jnode, "friend", modes->friend))
+		return false;
+
+	if (!mesh_db_write_mode(jnode, "proxy", modes->proxy))
+		return false;
+
+	/* Beaconing state */
+	if (!mesh_db_write_mode(jnode, "beacon", modes->beacon))
+		return false;
+
+	/* Sequence number */
+	json_object_object_add(jnode, "sequenceNumber",
+					json_object_new_int(node->seq_number));
+
+	/* Default TTL */
+	json_object_object_add(jnode, "defaultTTL",
+						json_object_new_int(node->ttl));
+
+	/* Elements */
+	jelements = json_object_new_array();
+	if (!jelements)
+		return false;
+
+	entry = l_queue_get_entries(node->elements);
+
+	for (; entry; entry = entry->next) {
+		struct mesh_db_element *ele = entry->data;
+		json_object *jelement, *jmodels;
+
+		jelement = json_object_new_object();
+
+		if (!jelement) {
+			json_object_put(jelements);
+			return false;
+		}
+
+		mesh_db_write_int(jelement, "elementIndex", ele->index);
+		mesh_db_write_uint16_hex(jelement, "location", ele->location);
+		json_object_array_add(jelements, jelement);
+
+		/* Models */
+		if (l_queue_isempty(ele->models))
+			continue;
+
+		jmodels = json_object_new_array();
+		if (!jmodels) {
+			json_object_put(jelements);
+			return false;
+		}
+
+		json_object_object_add(jelement, "models", jmodels);
+		l_queue_foreach(ele->models, add_model, jmodels);
+	}
+
+	json_object_object_add(jnode, "elements", jelements);
+
+	return true;
+}
diff --git a/mesh/mesh-db.h b/mesh/mesh-db.h
index 336302f28..40e60f72d 100644
--- a/mesh/mesh-db.h
+++ b/mesh/mesh-db.h
@@ -15,11 +15,8 @@
  *  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 {
@@ -103,9 +100,7 @@ typedef bool (*mesh_db_app_key_cb)(uint16_t idx, uint16_t net_idx,
 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_add_node(json_object *jnode, struct mesh_db_node *node);
 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,
@@ -124,6 +119,8 @@ bool mesh_db_write_app_key(json_object *jobj, uint16_t net_idx,
 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_uint32_hex(json_object *jobj, const char *desc,
+								uint32_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);
-- 
2.14.5


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

* [PATCH BlueZ v6 18/26] mesh: Restructure model services for multiple nodes
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (16 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 17/26] mesh: Restructure DB to support " Brian Gix
@ 2018-12-28 22:07 ` " Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 19/26] mesh: DBUS interface for Provisioning Agent Brian Gix
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

Handles checking that incoming Mesh messages obey all
authentication rules regarding subscriptions and encryption
key bindings on a per node basis, before forwarding via D-Bus
to external applications that are authorized to handle them.
---
 mesh/model.c | 813 ++++++++++++++++++++++++++++++++++++++++++-----------------
 mesh/model.h |  69 +++--
 2 files changed, 613 insertions(+), 269 deletions(-)

diff --git a/mesh/model.c b/mesh/model.c
index d50817843..1e302b73b 100644
--- a/mesh/model.c
+++ b/mesh/model.c
@@ -15,7 +15,6 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -24,18 +23,22 @@
 
 #include <sys/time.h>
 #include <ell/ell.h>
+#include <json-c/json.h>
 
 #include "mesh/mesh-defs.h"
 
 #include "mesh/mesh.h"
 #include "mesh/crypto.h"
 #include "mesh/node.h"
+#include "mesh/mesh-db.h"
 #include "mesh/net.h"
 #include "mesh/appkey.h"
-#include "mesh/model.h"
-#include "mesh/display.h"
 #include "mesh/cfgmod.h"
 #include "mesh/storage.h"
+#include "mesh/error.h"
+#include "mesh/dbus.h"
+#include "mesh/util.h"
+#include "mesh/model.h"
 
 struct mesh_model {
 	const struct mesh_model_ops *cbs;
@@ -67,6 +70,7 @@ struct mod_forward {
 	uint8_t ttl;
 	int8_t rssi;
 	bool szmict;
+	bool has_dst;
 	bool done;
 };
 
@@ -75,6 +79,14 @@ static struct l_queue *mesh_virtuals;
 static uint32_t virt_id_next = VIRTUAL_BASE;
 static struct timeval tx_start;
 
+static bool is_internal(uint32_t id)
+{
+	if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL)
+		return true;
+
+	return false;
+}
+
 static void unref_virt(void *data)
 {
 	struct mesh_virtual *virt = data;
@@ -94,6 +106,17 @@ static bool simple_match(const void *a, const void *b)
 	return a == b;
 }
 
+static bool has_binding(struct l_queue *bindings, uint16_t idx)
+{
+	const struct l_queue_entry *l;
+
+	for (l = l_queue_get_entries(bindings); l; l = l->next) {
+		if (L_PTR_TO_UINT(l->data) == idx)
+			return true;
+	}
+	return false;
+}
+
 static bool find_virt_by_id(const void *a, const void *b)
 {
 	const struct mesh_virtual *virt = a;
@@ -110,13 +133,39 @@ static bool find_virt_by_addr(const void *a, const void *b)
 	return memcmp(virt->addr, addr, 16) == 0;
 }
 
-static struct mesh_model *find_model(struct mesh_net *net, uint16_t addr,
+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);
+}
+
+static struct mesh_model *get_model(struct mesh_node *node, uint8_t ele_idx,
+						uint32_t id, int *status)
+{
+	struct l_queue *models;
+	struct mesh_model *model;
+
+	models = node_get_element_models(node, ele_idx, status);
+	if (!models) {
+		*status = MESH_STATUS_INVALID_ADDRESS;
+		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;
+}
+
+static struct mesh_model *find_model(struct mesh_node *node, 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);
 
@@ -126,7 +175,126 @@ static struct mesh_model *find_model(struct mesh_net *net, uint16_t addr,
 		return NULL;
 	}
 
-	return node_get_model(node, (uint8_t) ele_idx, mod_id, fail);
+	return get_model(node, (uint8_t) ele_idx, mod_id, fail);
+}
+
+static uint32_t convert_pub_period_to_ms(uint8_t pub_period)
+{
+	int n;
+
+	n = (pub_period & 0x3f);
+
+	switch (pub_period >> 6) {
+	default:
+		return n * 100;
+	case 2:
+		n *= 10;
+		/* Fall Through */
+	case 1:
+		return n * 1000;
+	case 3:
+		return n * 10 * 60 * 1000;
+	}
+}
+
+static struct l_dbus_message *create_config_update_msg(struct mesh_node *node,
+					uint8_t ele_idx, uint32_t id,
+					struct l_dbus_message_builder **builder)
+{
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+	const char *owner;
+	const char *path;
+	uint16_t model_id;
+
+	owner = node_get_owner(node);
+	path = node_get_element_path(node, ele_idx);
+	if (!path || !owner)
+		return NULL;
+
+	l_debug("Send \"UpdateModelConfiguration\"");
+	msg = l_dbus_message_new_method_call(dbus, owner, path,
+						MESH_ELEMENT_INTERFACE,
+						"UpdateModelConfiguration");
+
+	*builder = l_dbus_message_builder_new(msg);
+
+	model_id = (uint16_t) id;
+
+	l_dbus_message_builder_append_basic(*builder, 'q', &model_id);
+
+	l_dbus_message_builder_enter_array(*builder, "{sv}");
+
+	if ((id & VENDOR_ID_MASK) != VENDOR_ID_MASK) {
+		uint16_t vendor = id >> 16;
+		dbus_append_dict_entry_basic(*builder, "Vendor", "q", &vendor);
+	}
+
+	return msg;
+}
+
+static void config_update_model_pub_period(struct mesh_node *node,
+					uint8_t ele_idx, uint32_t model_id,
+					uint32_t period)
+{
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+	struct l_dbus_message_builder *builder;
+
+	msg = create_config_update_msg(node, ele_idx, model_id, &builder);
+	if (!msg)
+		return;
+
+	dbus_append_dict_entry_basic(builder, "PublicationPeriod", "u",
+								&period);
+
+	l_dbus_message_builder_leave_array(builder);
+	if (l_dbus_message_builder_finalize(builder))
+		l_dbus_send(dbus, msg);
+
+	l_dbus_message_builder_destroy(builder);
+}
+
+static void append_dict_uint16_array(struct l_dbus_message_builder *builder,
+					struct l_queue *q, const char *key)
+{
+	const struct l_queue_entry *entry;
+
+	l_dbus_message_builder_enter_dict(builder, "sv");
+	l_dbus_message_builder_append_basic(builder, 's', key);
+	l_dbus_message_builder_enter_variant(builder, "aq");
+	l_dbus_message_builder_enter_array(builder, "q");
+
+	for (entry = l_queue_get_entries(q); entry; entry = entry->next) {
+		uint16_t value = (uint16_t) L_PTR_TO_UINT(entry->data);
+
+		l_dbus_message_builder_append_basic(builder,'q', &value);
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_variant(builder);
+	l_dbus_message_builder_leave_dict(builder);
+}
+
+static void config_update_model_bindings(struct mesh_node *node,
+							struct mesh_model *mod)
+{
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+	struct l_dbus_message_builder *builder;
+
+	msg = create_config_update_msg(node, mod->ele_idx, mod->id,
+								&builder);
+	if (!msg)
+		return;
+
+	append_dict_uint16_array(builder, mod->bindings, "Bindings");
+
+	l_dbus_message_builder_leave_array(builder);
+	if (l_dbus_message_builder_finalize(builder))
+		l_dbus_send(dbus, msg);
+
+	l_dbus_message_builder_destroy(builder);
 }
 
 static void forward_model(void *a, void *b)
@@ -135,22 +303,22 @@ static void forward_model(void *a, void *b)
 	struct mod_forward *fwd = b;
 	struct mesh_virtual *virt;
 	uint32_t dst;
-	bool has_dst = false;
-
-	if (!mod->cbs || !mod->cbs->recv)
-		return;
+	bool result;
 
 	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)))
+	if (fwd->idx != APP_IDX_DEV && !has_binding(mod->bindings, fwd->idx))
 		return;
 
 	dst = fwd->dst;
 	if (dst == fwd->unicast || IS_ALL_NODES(dst))
-		has_dst = true;
+		fwd->has_dst = true;
 	else if (fwd->virt) {
 		virt = l_queue_find(mod->virtuals, simple_match, fwd->virt);
+
+		/* Check that this is not own publication */
+		if (mod->pub && (virt && virt->id == mod->pub->addr))
+			return;
+
 		if (virt) {
 			/*
 			 * Map Virtual addresses to a usable namespace that
@@ -158,42 +326,38 @@ static void forward_model(void *a, void *b)
 			 * (multiple Virtual Addresses that map to the same
 			 * u16 OTA addr)
 			 */
-			has_dst = true;
+			fwd->has_dst = true;
 			dst = virt->id;
 		}
 	} else {
 		if (l_queue_find(mod->subs, simple_match, L_UINT_TO_PTR(dst)))
-			has_dst = true;
+			fwd->has_dst = true;
 	}
 
+	if (!fwd->has_dst)
+		return;
 
-	if (!has_dst)
+	/* Return, if this is not a internal model */
+	if (!mod->cbs)
 		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)
-	 */
+	result = false;
+
 	if (mod->cbs->recv)
-		mod->cbs->recv(fwd->src, dst, fwd->unicast, fwd->idx,
+		result = mod->cbs->recv(fwd->src, dst, fwd->unicast, fwd->idx,
 				fwd->data, fwd->size, fwd->ttl, mod->user_data);
 
-	if (dst == fwd->unicast)
+	if (dst == fwd->unicast && result)
 		fwd->done = true;
 }
 
-static int dev_packet_decrypt(struct mesh_net *net, const uint8_t *data,
+static int dev_packet_decrypt(struct mesh_node *node, 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;
@@ -241,32 +405,32 @@ static void cmplt(uint16_t remote, uint8_t status,
 	struct timeval tx_end;
 
 	if (status)
-		l_info("Tx-->%4.4x (%d octets) Failed (%d)",
+		l_debug("Tx-->%4.4x (%d octets) Failed (%d)",
 				remote, size, status);
 	else
-		l_info("Tx-->%4.4x (%d octets) Succeeded", remote, size);
+		l_debug("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",
+		l_debug("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",
+			l_debug("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",
+			l_debug("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)
+static bool pub_frnd_cred(struct mesh_node *node, uint16_t src, uint32_t mod_id)
 {
-	struct mesh_model *mod = find_model(net, src, mod_id, NULL);
+	struct mesh_model *mod = find_model(node, src, mod_id, NULL);
 
 	if (!mod || !mod->pub)
 		return false;
@@ -274,17 +438,16 @@ static bool pub_frnd_cred(struct mesh_net *net, uint16_t src, uint32_t mod_id)
 	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)
+static bool msg_send(struct mesh_node *node, bool credential, 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;
+	bool ret = false;
 	uint32_t iv_index, seq_num;
 	uint8_t *out;
 	bool szmic = false;
 	uint16_t out_len = msg_len + sizeof(uint32_t);
+	struct mesh_net *net = node_get_net(node);
 
 	/* Use large MIC if it doesn't affect segmentation */
 	if (msg_len > 11 && msg_len <= 376) {
@@ -309,7 +472,7 @@ static unsigned int msg_send(struct mesh_net *net, uint32_t mod_id,
 
 	/* print_packet("Encrypted with", key, 16); */
 
-	ret = mesh_net_app_send(net, pub_frnd_cred(net, src, mod_id),
+	ret = mesh_net_app_send(net, credential,
 				src, dst, key_id, ttl,
 				seq_num, iv_index,
 				szmic,
@@ -320,79 +483,100 @@ done:
 	return ret;
 }
 
-static void model_unbind_idx(void *a, void *b)
+static void remove_pub(struct mesh_node *node, struct mesh_model *mod)
 {
-	struct mesh_model *mod = a;
-	uint16_t idx = L_PTR_TO_UINT(b);
+	l_free(mod->pub);
+	mod->pub = NULL;
 
-	if (idx == mod->pub->idx) {
-		mod->pub->addr = UNASSIGNED_ADDRESS;
-		/*
-		 * TODO: callback for internal model or
-		 * D-Bus signal/method "model publication changed" (TBD)
-		 */
-	}
+	/* TODO: remove from storage */
+
+	if (!mod->cbs)
+		/* External models */
+		config_update_model_pub_period(node, mod->ele_idx, mod->id, 0);
+	else if (mod->cbs && mod->cbs->pub)
+		/* Internal models */
+		mod->cbs->pub(NULL);
+}
 
-	l_queue_remove(mod->bindings, b);
+static void model_unbind_idx(struct mesh_node *node, struct mesh_model *mod,
+								uint16_t idx)
+{
+	l_queue_remove(mod->bindings, L_UINT_TO_PTR(idx));
 
-	if (mod->cbs->bind)
+	if (!mod->cbs)
+		/* External model */
+		config_update_model_bindings(node, mod);
+	else if (mod->cbs->bind)
+		/* Internal model */
 		mod->cbs->bind(idx, ACTION_DELETE);
+
+	if (mod->pub && idx != mod->pub->idx)
+		return;
+
+	/* Remove model publication if the publication key is unbound */
+	remove_pub(node, mod);
 }
 
-static int model_bind_idx(struct mesh_model *mod, uint16_t idx)
+static void model_bind_idx(struct mesh_node *node, struct mesh_model *mod,
+								uint16_t idx)
 {
-	if (l_queue_length(mod->bindings) >= MAX_BINDINGS)
-		return MESH_STATUS_INSUFF_RESOURCES;
+	l_queue_push_tail(mod->bindings, L_UINT_TO_PTR(idx));
 
-	if (!l_queue_push_tail(mod->bindings, L_UINT_TO_PTR(idx)))
-		return MESH_STATUS_INSUFF_RESOURCES;
+	l_debug("Add %4.4x to model %8.8x", idx, mod->id);
 
-	if (mod->cbs->bind)
+	if (!mod->cbs)
+		/* External model */
+		config_update_model_bindings(node, mod);
+	else if (mod->cbs->bind)
+		/* Internal model */
 		mod->cbs->bind(idx, ACTION_ADD);
-
-	return MESH_STATUS_SUCCESS;
-
 }
 
-static int update_binding(struct mesh_net *net, uint16_t addr, uint32_t id,
+static int update_binding(struct mesh_node *node, uint16_t addr, uint32_t id,
 				uint16_t app_idx, bool unbind)
 {
 	int fail;
 	struct mesh_model *mod;
-	int status;
+	bool is_present;
 
-	mod = find_model(net, addr, id, &fail);
+	mod = find_model(node, addr, id, &fail);
 	if (!mod) {
-		l_info("model not found");
+		l_debug("Model not found");
 		return fail;
 	}
 
+	id = (id >= VENDOR_ID_MASK) ? (id & 0xffff) : id;
+
 	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))
+	if (!appkey_have_key(node_get_net(node), app_idx))
 		return MESH_STATUS_INVALID_APPKEY;
 
+	is_present = has_binding(mod->bindings, app_idx);
+
+	if (!is_present && unbind)
+		return MESH_STATUS_SUCCESS;
+
+	if (is_present && !unbind)
+		return MESH_STATUS_SUCCESS;
+
 	if (unbind) {
-		model_unbind_idx(mod, &app_idx);
+		model_unbind_idx(node, mod, app_idx);
 
-		if (!storage_model_bind(net, addr, id, app_idx, true))
+		if (!storage_model_bind(node, 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 (l_queue_length(mod->bindings) >= MAX_BINDINGS)
+		return MESH_STATUS_INSUFF_RESOURCES;
 
-	if (!storage_model_bind(net, addr, id, app_idx, false)) {
-		model_unbind_idx(mod, &app_idx);
+	if (!storage_model_bind(node, addr, id, app_idx, false))
 		return MESH_STATUS_STORAGE_FAIL;
-	}
+
+	model_bind_idx(node, mod, app_idx);
 
 	return MESH_STATUS_SUCCESS;
 
@@ -428,6 +612,7 @@ static int set_pub(struct mesh_model *mod, const uint8_t *mod_addr,
 		}
 	}
 
+	mod->pub = l_new(struct mesh_model_pub, 1);
 	if (b_virt) {
 		virt = l_queue_find(mesh_virtuals, find_virt_by_addr, mod_addr);
 		if (!virt) {
@@ -511,14 +696,57 @@ static int add_sub(struct mesh_net *net, struct mesh_model *mod,
 
 	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);
+	l_debug("Added %4.4x", grp);
+
+	mesh_net_dst_reg(net, grp);
 
 	return MESH_STATUS_SUCCESS;
 }
 
-bool mesh_model_rx(struct mesh_net *net, bool szmict, uint32_t seq0,
+static void send_msg_rcvd(struct mesh_node *node, uint8_t ele_idx, bool is_sub,
+					uint16_t src, uint16_t key_idx,
+					uint16_t size, const uint8_t *data)
+{
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+	struct l_dbus_message_builder *builder;
+	const char *owner;
+	const char *path;
+
+	owner = node_get_owner(node);
+	path = node_get_element_path(node, ele_idx);
+	if (!path || !owner)
+		return;
+
+	l_debug("Send \"MessageReceived\"");
+
+	msg = l_dbus_message_new_method_call(dbus, owner, path,
+				MESH_ELEMENT_INTERFACE, "MessageReceived");
+
+	builder = l_dbus_message_builder_new(msg);
+
+	if (!l_dbus_message_builder_append_basic(builder, 'q', &src))
+		goto error;
+
+	if (!l_dbus_message_builder_append_basic(builder, 'q', &key_idx))
+		goto error;
+
+	if (!l_dbus_message_builder_append_basic(builder, 'b', &is_sub))
+		goto error;
+
+	if (!dbus_append_byte_array(builder, data, size))
+		goto error;
+
+	if (!l_dbus_message_builder_finalize(builder))
+		goto error;
+
+	l_dbus_send(dbus, msg);
+
+error:
+	l_dbus_message_builder_destroy(builder);
+}
+
+bool mesh_model_rx(struct mesh_node *node, 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)
@@ -531,30 +759,25 @@ bool mesh_model_rx(struct mesh_net *net, bool szmict, uint32_t seq0,
 		.size = size - (szmict ? 8 : 4),
 		.ttl = ttl,
 		.virt = NULL,
-		.done = false,
 	};
-
-	struct mesh_node *node;
+	struct mesh_net *net = node_get_net(node);
 	uint8_t num_ele;
 	int decrypt_idx, i, ele_idx;
 	uint16_t addr;
 	struct mesh_virtual *decrypt_virt = NULL;
+	bool result = false;
+	bool is_subscription;
 
 	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;
@@ -566,7 +789,7 @@ bool mesh_model_rx(struct mesh_net *net, bool szmict, uint32_t seq0,
 	 * 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,
+		decrypt_idx = dev_packet_decrypt(node, data, size, szmict, src,
 						dst, key_id, seq0, iv_index,
 						clear_text);
 	else if ((dst & 0xc000) == 0x8000)
@@ -582,18 +805,18 @@ bool mesh_model_rx(struct mesh_net *net, bool szmict, uint32_t seq0,
 
 	if (decrypt_idx < 0) {
 		l_error("model.c - Failed to decrypt application payload");
-		forward.done = false;
+		result = 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);
+		uint16_t crpl = node_get_crpl(node);
 
 		if (appkey_msg_in_replay_cache(net, (uint16_t) decrypt_idx, src,
 							crpl, seq, iv_index)) {
-			forward.done = true;
+			result = true;
 			goto done;
 		}
 	}
@@ -608,59 +831,86 @@ bool mesh_model_rx(struct mesh_net *net, bool szmict, uint32_t seq0,
 	if (!num_ele || IS_UNASSIGNED(addr))
 		goto done;
 
+	is_subscription = !(IS_UNICAST(dst));
+
 	for (i = 0; i < num_ele; i++) {
 		struct l_queue *models;
 
-		if (dst < 0x8000 && ele_idx != i)
+		if (!is_subscription && ele_idx != i)
 			continue;
 
 		forward.unicast = addr + i;
+		forward.has_dst = false;
+
 		models = node_get_element_models(node, i, NULL);
+
+		/* Internal models */
 		l_queue_foreach(models, forward_model, &forward);
 
-		if (dst < 0x8000 && ele_idx == i)
+		/*
+		 * Cycle through external models if the message has not been
+		 * handled by internal models
+		 */
+		if (forward.has_dst && !forward.done)
+			send_msg_rcvd(node, i, is_subscription, src,
+					forward.idx, forward.size,
+					forward.data);
+
+		/*
+		 * Either the message has been processed internally or
+		 * has been passed on to an external model.
+		 */
+		result = forward.has_dst | forward.done;
+
+		/* If the message was to unicast address, we are done */
+		if (!is_subscription && ele_idx == i)
 			break;
 	}
+
 done:
 	l_free(clear_text);
-	return forward.done;
+	return result;
 }
 
-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,
+int mesh_model_publish(struct mesh_node *node, uint32_t mod_id,
+				uint16_t src, uint8_t ttl,
 				const void *msg, uint16_t msg_len)
 {
+	struct mesh_net *net = node_get_net(node);
 	struct mesh_model *mod;
+	uint32_t target;
 	uint8_t *aad = NULL;
 	uint16_t dst;
 	uint8_t key_id;
 	const uint8_t *key;
+	bool result;
 
 	/* print_packet("Mod Tx", msg, msg_len); */
 
 	if (!net || msg_len > 380)
-		return 0;
+		return MESH_ERROR_INVALID_ARGS;
 
 	/* 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);
+	mod = find_model(node, src, mod_id, NULL);
 	if (!mod) {
-		l_info("model %x not found", mod_id);
-		return 0;
+		l_debug("model %x not found", mod_id);
+		return MESH_ERROR_NOT_FOUND;
+	}
+
+	if (!mod->pub) {
+		l_debug("publication doesn't exist (model %x)", mod_id);
+		return MESH_ERROR_DOES_NOT_EXIST;
 	}
 
 	gettimeofday(&tx_start, NULL);
 
-	if (target == USE_PUB_VALUE) {
-		target = mod->pub->addr;
-		app_idx = mod->pub->idx;
-	}
+	target = mod->pub->addr;
 
 	if (IS_UNASSIGNED(target))
-		return 0;
+		return false;
 
 	if (target >= VIRTUAL_BASE) {
 		struct mesh_virtual *virt = l_queue_find(mesh_virtuals,
@@ -668,101 +918,139 @@ unsigned int mesh_model_send(struct mesh_net *net, uint32_t mod_id,
 				L_UINT_TO_PTR(target));
 
 		if (!virt)
-			return 0;
+			return false;
 
 		aad = virt->addr;
 		dst = virt->ota;
-	} else
+	} else {
 		dst = target;
+	}
+
+	l_debug("publish dst=%x", dst);
+
+	key = appkey_get_key(net, mod->pub->idx, &key_id);
+	if (!key) {
+		l_debug("no app key for (%x)", mod->pub->idx);
+		return false;
+	}
+
+	l_debug("(%x) %p", mod->pub->idx, key);
+	l_debug("key_id %x", key_id);
+
+	result = msg_send(node, pub_frnd_cred(node, src, mod_id), src,
+				dst, key_id, key, aad, ttl, msg, msg_len);
+
+	return result ? MESH_ERROR_NONE : MESH_ERROR_FAILED;
+
+}
+
+bool mesh_model_send(struct mesh_node *node, uint16_t src, uint16_t target,
+					uint16_t app_idx, uint8_t ttl,
+					const void *msg, uint16_t msg_len)
+{
+	uint8_t key_id;
+	const uint8_t *key;
+
+	/* print_packet("Mod Tx", msg, msg_len); */
+
+	/* If SRC is 0, use the Primary Element */
+	if (src == 0)
+		src = node_get_primary(node);
+
+	gettimeofday(&tx_start, NULL);
 
-	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 (IS_UNASSIGNED(target))
+		return false;
+
+	if (app_idx == APP_IDX_DEV) {
+		key = node_get_device_key(node);
 		if (!key)
-			return 0;
+			return false;
 
 		l_debug("(%x)", app_idx);
 		key_id = APP_ID_DEV;
 	} else {
-		key = appkey_get_key(net, app_idx, &key_id);
+		key = appkey_get_key(node_get_net(node), app_idx, &key_id);
 		if (!key) {
 			l_debug("no app key for (%x)", app_idx);
-			return 0;
+			return false;
 		}
 
 		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,
+	return msg_send(node, false, src, target, key_id, key, NULL, ttl,
 			msg, msg_len);
-
 }
 
-int mesh_model_pub_set(struct mesh_net *net, uint16_t addr, uint32_t id,
+int mesh_model_pub_set(struct mesh_node *node, 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;
+	int ele_idx;
 	struct mesh_model *mod;
-	struct mesh_node *node;
+	int result;
 
-	node = mesh_net_local_node_get(net);
-	if (node)
-		ele_idx = node_get_element_idx(node, addr);
+	ele_idx = node_get_element_idx(node, addr);
 
-	if (!node || ele_idx < 0) {
+	if (ele_idx < 0) {
 		fail = MESH_STATUS_INVALID_ADDRESS;
 		return false;
 	}
 
-	mod = node_get_model(node, (uint8_t) ele_idx, id, &fail);
+	mod = 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))
+	if (!appkey_have_key(node_get_net(node), idx))
 		return MESH_STATUS_INVALID_APPKEY;
 
-	return set_pub(mod, mod_addr, idx, cred_flag, ttl, period, retransmit,
+	result = set_pub(mod, mod_addr, idx, cred_flag, ttl, period, retransmit,
 								b_virt, dst);
+
+	if (result != MESH_STATUS_SUCCESS)
+		return result;
+
+	/* TODO: save to storage */
+
 	/*
-	 * TODO: Add standardized Publication Change notification to model
-	 * definition
+	 * If the publication address is set to unassigned address value,
+	 * remove publication
 	 */
+	if (IS_UNASSIGNED(*dst))
+		remove_pub(node, mod);
+
+	/* Internal model, call registered callbacks */
+	if (mod->cbs && mod->cbs->pub) {
+		mod->cbs->pub(mod->pub);
+		return MESH_STATUS_SUCCESS;
+	}
+
+	/* External model */
+	config_update_model_pub_period(node, ele_idx, id,
+					convert_pub_period_to_ms(period));
+
+	return MESH_STATUS_SUCCESS;
 }
 
-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_pub *mesh_model_pub_get(struct mesh_node *node,
+				 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);
+	mod = 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;
@@ -774,33 +1062,39 @@ void mesh_model_free(void *data)
 	l_free(mod);
 }
 
-struct mesh_model *mesh_model_new(uint8_t ele_idx, uint32_t id, bool vendor)
+static struct mesh_model *model_new(uint8_t ele_idx, uint32_t id)
 {
 	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 *mesh_model_new(uint8_t ele_idx, uint16_t id)
 {
-	struct mesh_model *mod = data;
+	return model_new(ele_idx, id | VENDOR_ID_MASK);
+}
+
+struct mesh_model *mesh_model_vendor_new(uint8_t ele_idx, uint16_t vendor_id,
+								uint16_t mod_id)
+{
+	uint32_t id = mod_id | (vendor_id << 16);
+
+	return model_new(ele_idx, id);
+}
+
+/* Internal models only */
+static void restore_model_state(struct mesh_model *mod)
+{
+
 	const struct mesh_model_ops *cbs;
 	const struct l_queue_entry *b;
 
 	cbs = mod->cbs;
+	if (!cbs)
+		return;
 
 	if (l_queue_isempty(mod->bindings) || !mod->cbs->bind) {
 		for (b = l_queue_get_entries(mod->bindings); b; b = b->next) {
@@ -812,63 +1106,64 @@ static void restore_model_state(void *data)
 
 	if (mod->pub && cbs->pub)
 		cbs->pub(mod->pub);
+
 }
 
-bool mesh_model_vendor_register(struct mesh_net *net, uint8_t ele_idx,
+uint32_t mesh_model_get_model_id(const struct mesh_model *model)
+{
+	return model->id;
+}
+
+/* This registers an internal model, i.e. implemented within meshd */
+bool mesh_model_register(struct mesh_node *node, 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;
+	/* Internal models are always SIG models */
+	mod_id = VENDOR_ID_MASK | mod_id;
 
-	mod = node_get_model(node, ele_idx, mod_id, NULL);
+	mod = 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);
+	restore_model_state(mod);
 
 	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)
+void mesh_model_app_key_delete(struct mesh_node *node, struct l_queue *models,
+							uint16_t app_idx)
 {
-	uint32_t id = VENDOR_ID_MASK | mod_id;
+	const struct l_queue_entry *entry = l_queue_get_entries(models);
 
-	return mesh_model_vendor_register(net, ele_idx, id, cbs, user_data);
-}
+	for (; entry; entry = entry->next) {
+		struct mesh_model *model = entry->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));
+		model_unbind_idx(node, model, app_idx);
+	}
 }
 
-int mesh_model_binding_del(struct mesh_net *net, uint16_t addr, uint32_t id,
+int mesh_model_binding_del(struct mesh_node *node, 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);
+	return update_binding(node, addr, id, app_idx, true);
 }
 
-int mesh_model_binding_add(struct mesh_net *net, uint16_t addr, uint32_t id,
+int mesh_model_binding_add(struct mesh_node *node, 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);
+	return update_binding(node, addr, id, app_idx, false);
 }
 
-int mesh_model_get_bindings(struct mesh_net *net, uint16_t addr, uint32_t id,
+int mesh_model_get_bindings(struct mesh_node *node, uint16_t addr, uint32_t id,
 				uint8_t *buf, uint16_t buf_size, uint16_t *size)
 {
 	int fail;
@@ -878,7 +1173,7 @@ int mesh_model_get_bindings(struct mesh_net *net, uint16_t addr, uint32_t id,
 	uint32_t idx_pair;
 	int i;
 
-	mod = find_model(net, addr, id, &fail);
+	mod = find_model(node, addr, id, &fail);
 
 	if (!mod) {
 		*size = 0;
@@ -922,7 +1217,7 @@ done:
 	return MESH_STATUS_SUCCESS;
 }
 
-int mesh_model_sub_get(struct mesh_net *net, uint16_t addr, uint32_t id,
+int mesh_model_sub_get(struct mesh_node *node, uint16_t addr, uint32_t id,
 			uint8_t *buf, uint16_t buf_size, uint16_t *size)
 {
 	int fail = MESH_STATUS_SUCCESS;
@@ -930,7 +1225,7 @@ int mesh_model_sub_get(struct mesh_net *net, uint16_t addr, uint32_t id,
 	struct mesh_model *mod;
 	const struct l_queue_entry *entry;
 
-	mod = find_model(net, addr, id, &fail);
+	mod = find_model(node, addr, id, &fail);
 	if (!mod)
 		return fail;
 
@@ -951,32 +1246,29 @@ int mesh_model_sub_get(struct mesh_net *net, uint16_t addr, uint32_t id,
 	return MESH_STATUS_SUCCESS;
 }
 
-int mesh_model_sub_add(struct mesh_net *net, uint16_t addr, uint32_t id,
+int mesh_model_sub_add(struct mesh_node *node, 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);
+	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);
+	mod = get_model(node, (uint8_t) ele_idx, id, &fail);
 	if (!mod)
 		return fail;
 
-	return add_sub(net, mod, group, b_virt, dst);
+	return add_sub(node_get_net(node), 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,
+int mesh_model_sub_ovr(struct mesh_node *node, uint16_t addr, uint32_t id,
 			const uint8_t *group, bool b_virt, uint16_t *dst)
 {
 	int fail = MESH_STATUS_SUCCESS;
@@ -984,7 +1276,7 @@ int mesh_model_sub_ovr(struct mesh_net *net, uint16_t addr, uint32_t id,
 	struct mesh_virtual *virt;
 	struct mesh_model *mod;
 
-	mod = find_model(net, addr, id, &fail);
+	mod = find_model(node, addr, id, &fail);
 	if (!mod)
 		return fail;
 
@@ -1009,7 +1301,7 @@ int mesh_model_sub_ovr(struct mesh_net *net, uint16_t addr, uint32_t id,
 		}
 	}
 
-	fail = mesh_model_sub_add(net, addr, id, group, b_virt, dst);
+	fail = mesh_model_sub_add(node, addr, id, group, b_virt, dst);
 
 	if (fail) {
 		/* Adding new group failed, so revert to old list */
@@ -1019,6 +1311,7 @@ int mesh_model_sub_ovr(struct mesh_net *net, uint16_t addr, uint32_t id,
 		mod->virtuals = virtuals;
 	} else {
 		const struct l_queue_entry *entry;
+		struct mesh_net *net = node_get_net(node);
 
 		entry = l_queue_get_entries(subs);
 		for (; entry; entry = entry->next)
@@ -1032,14 +1325,14 @@ int mesh_model_sub_ovr(struct mesh_net *net, uint16_t addr, uint32_t id,
 	return fail;
 }
 
-int mesh_model_sub_del(struct mesh_net *net, uint16_t addr, uint32_t id,
+int mesh_model_sub_del(struct mesh_node *node, 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);
+	mod = find_model(node, addr, id, &fail);
 	if (!mod)
 		return fail;
 
@@ -1062,18 +1355,19 @@ int mesh_model_sub_del(struct mesh_net *net, uint16_t addr, uint32_t id,
 	*dst = grp;
 
 	if (l_queue_remove(mod->subs, L_UINT_TO_PTR(grp)))
-		mesh_net_dst_unreg(net, grp);
+		mesh_net_dst_unreg(node_get_net(node), grp);
 
 	return MESH_STATUS_SUCCESS;
 }
 
-int mesh_model_sub_del_all(struct mesh_net *net, uint16_t addr, uint32_t id)
+int mesh_model_sub_del_all(struct mesh_node *node, uint16_t addr, uint32_t id)
 {
 	int fail = MESH_STATUS_SUCCESS;
 	struct mesh_model *mod;
 	const struct l_queue_entry *entry;
+	struct mesh_net *net = node_get_net(node);
 
-	mod = find_model(net, addr, id, &fail);
+	mod = find_model(node, addr, id, &fail);
 	if (!mod)
 		return fail;
 
@@ -1088,15 +1382,22 @@ int mesh_model_sub_del_all(struct mesh_net *net, uint16_t addr, uint32_t id)
 	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 *mesh_model_setup(struct mesh_node *node, uint8_t ele_idx,
+								void *data)
 {
+	struct mesh_db_model *db_mod = data;
 	struct mesh_model *mod;
+	struct mesh_net *net;
 	uint32_t i;
 
-	mod = mesh_model_new(ele_idx, db_mod->id, db_mod->vendor);
-	if (!mod)
+	if (db_mod->num_bindings > MAX_BINDINGS) {
+		l_warn("Binding list too long %u (max %u)",
+					db_mod->num_bindings, MAX_BINDINGS);
 		return NULL;
+	}
+
+	mod = model_new(ele_idx, db_mod->vendor ? db_mod->id :
+						db_mod->id | VENDOR_ID_MASK);
 
 	/* Implicitly bind config server model to device key */
 	if (db_mod->id == CONFIG_SRV_MODEL) {
@@ -1113,35 +1414,27 @@ struct mesh_model *mesh_model_init(struct mesh_net *net, uint8_t ele_idx,
 		return mod;
 	}
 
+	net = node_get_net(node);
+
 	/* 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;
-			}
-		}
+		for (i = 0; i < db_mod->num_bindings; i++)
+			model_bind_idx(node, mod, db_mod->bindings[i]);
 	}
 
 	/* Add publication if present */
 	if (db_mod->pub) {
-		uint16_t mod_addr;
-		uint8_t *dst;
+		struct mesh_db_pub *pub = db_mod->pub;
+		uint8_t mod_addr[2];
+		uint8_t *pub_addr;
 
-		l_put_le16(db_mod->pub->addr, &mod_addr);
-		dst = db_mod->pub->virt ? db_mod->pub->virt_addr :
-							(uint8_t *) &mod_addr;
+		/* Add publication */
+		l_put_le16(pub->addr, &mod_addr);
+		pub_addr = pub->virt ? 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) !=
+		if (set_pub(mod, pub_addr, pub->idx, pub->credential, pub->ttl,
+			pub->period, pub->retransmit, pub->virt, NULL) !=
 							MESH_STATUS_SUCCESS) {
 			mesh_model_free(mod);
 			return NULL;
@@ -1192,7 +1485,7 @@ uint16_t mesh_model_opcode_set(uint32_t opcode, uint8_t *buf)
 		return 3;
 	}
 
-	l_info("Illegal Opcode %x", opcode);
+	l_debug("Illegal Opcode %x", opcode);
 	return 0;
 }
 
@@ -1238,7 +1531,7 @@ bool mesh_model_opcode_get(const uint8_t *buf, uint16_t size,
 	return true;
 }
 
-void mesh_model_add_virtual(struct mesh_net *net, const uint8_t *v)
+void mesh_model_add_virtual(struct mesh_node *node, const uint8_t *v)
 {
 	struct mesh_virtual *virt = l_queue_find(mesh_virtuals,
 						find_virt_by_addr, v);
@@ -1263,7 +1556,7 @@ void mesh_model_add_virtual(struct mesh_net *net, const uint8_t *v)
 	l_queue_push_head(mesh_virtuals, virt);
 }
 
-void mesh_model_del_virtual(struct mesh_net *net, uint32_t va24)
+void mesh_model_del_virtual(struct mesh_node *node, uint32_t va24)
 {
 	struct mesh_virtual *virt = l_queue_remove_if(mesh_virtuals,
 						find_virt_by_id,
@@ -1272,3 +1565,55 @@ void mesh_model_del_virtual(struct mesh_net *net, uint32_t va24)
 	if (virt)
 		unref_virt(virt);
 }
+
+void model_build_config(void *model, void *msg_builder)
+{
+	struct l_dbus_message_builder *builder = msg_builder;
+	struct mesh_model *mod = model;
+	uint16_t id;
+
+	if (is_internal(mod->id))
+		return;
+
+	if (!l_queue_length(mod->subs) && !l_queue_length(mod->virtuals) &&
+				!mod->pub && !l_queue_length(mod->bindings))
+		return;
+
+	l_dbus_message_builder_enter_struct(builder, "qa{sv}");
+
+	/* Model id */
+	id = mod->id & 0xffff;
+	l_dbus_message_builder_append_basic(builder, 'q', &id);
+
+	l_dbus_message_builder_enter_array(builder, "{sv}");
+
+	/* For vendor models, add vendor id */
+	if ((mod->id & VENDOR_ID_MASK) != VENDOR_ID_MASK) {
+		uint16_t vendor = mod->id >> 16;
+		dbus_append_dict_entry_basic(builder, "Vendor", "q", &vendor);
+	}
+
+	/* Model bindings, if present */
+	if (l_queue_length(mod->bindings))
+		append_dict_uint16_array(builder, mod->bindings, "Bindings");
+
+	/* Model periodic publication interval, if present */
+	if (mod->pub) {
+		uint32_t period = convert_pub_period_to_ms(mod->pub->period);
+		dbus_append_dict_entry_basic(builder, "PublicationPeriod", "u",
+								&period);
+	}
+
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_leave_struct(builder);
+}
+
+void mesh_model_init(void)
+{
+	mesh_virtuals = l_queue_new();
+}
+
+void mesh_model_cleanup(void)
+{
+	l_queue_destroy(mesh_virtuals, l_free);
+}
diff --git a/mesh/model.h b/mesh/model.h
index 3a41bd722..fd0b25f31 100644
--- a/mesh/model.h
+++ b/mesh/model.h
@@ -15,11 +15,8 @@
  *  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
@@ -85,62 +82,64 @@ struct mesh_model_ops {
 	mesh_model_sub_cb sub;
 };
 
-struct mesh_model *mesh_model_new(uint8_t ele_idx, uint32_t id, bool vendor);
+struct mesh_model *mesh_model_new(uint8_t ele_idx, uint16_t mod_id);
+struct mesh_model *mesh_model_vendor_new(uint8_t ele_idx, uint16_t vendor_id,
+							uint16_t mod_id);
 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,
+bool mesh_model_register(struct mesh_node *node, 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,
+struct mesh_model_pub *mesh_model_pub_get(struct mesh_node *node,
+				uint8_t ele_idx, uint32_t mod_id, int *status);
+int mesh_model_pub_set(struct mesh_node *node, 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);
+struct mesh_model *mesh_model_setup(struct mesh_node *node, uint8_t ele_idx,
+								void *data);
 
-int mesh_model_binding_add(struct mesh_net *net, uint16_t addr, uint32_t id,
+int mesh_model_binding_add(struct mesh_node *node, 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,
+int mesh_model_binding_del(struct mesh_node *node, uint16_t addr, uint32_t id,
 						uint16_t idx);
-int mesh_model_get_bindings(struct mesh_net *net, uint16_t addr, uint32_t id,
+int mesh_model_get_bindings(struct mesh_node *node, 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,
+int mesh_model_sub_add(struct mesh_node *node, 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,
+int mesh_model_sub_del(struct mesh_node *node, 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,
+int mesh_model_sub_del_all(struct mesh_node *node, uint16_t addr, uint32_t id);
+int mesh_model_sub_ovr(struct mesh_node *node, 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,
+int mesh_model_sub_get(struct mesh_node *node, 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,
+bool mesh_model_send(struct mesh_node *node, uint16_t src, uint16_t target,
+					uint16_t app_idx, uint8_t ttl,
+					const void *msg, uint16_t msg_len);
+int mesh_model_publish(struct mesh_node *node, uint32_t mod_id,
+				uint16_t src, uint8_t ttl,
 				const void *msg, uint16_t msg_len);
-
-bool mesh_model_rx(struct mesh_net *net, bool szmict, uint32_t seq0,
+bool mesh_model_rx(struct mesh_node *node, 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,
+void mesh_model_app_key_generate_new(struct mesh_node *node, uint16_t net_idx);
+void mesh_model_app_key_delete(struct mesh_node *node, 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);
+struct l_queue *mesh_model_get_appkeys(struct mesh_node *node);
+void mesh_model_add_virtual(struct mesh_node *node, const uint8_t *v);
+void mesh_model_del_virtual(struct mesh_node *node, uint32_t va24);
+void mesh_model_list_virtual(struct mesh_node *node);
 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);
+void model_build_config(void *model, void *msg_builder);
+
+void mesh_model_init(void);
+void mesh_model_cleanup(void);
-- 
2.14.5


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

* [PATCH BlueZ v6 19/26] mesh: DBUS interface for Provisioning Agent
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (17 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 18/26] mesh: Restructure model services for " Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 20/26] mesh: restructure App Key storage Brian Gix
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

Implements the time limited and user interaction D-Bus API as
required for Out-Of-Band authentication during provisioning,
and as described in doc/mesh-api.txt.
---
 mesh/agent.c | 665 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
 mesh/agent.h |  69 +++++--
 2 files changed, 589 insertions(+), 145 deletions(-)

diff --git a/mesh/agent.c b/mesh/agent.c
index 1da10b7bd..c6ff11802 100644
--- a/mesh/agent.c
+++ b/mesh/agent.c
@@ -15,215 +15,632 @@
  *  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 "mesh/util.h"
+#include "mesh/mesh.h"
+#include "mesh/provision.h"
+#include "mesh/error.h"
+#include "mesh/dbus.h"
 #include "mesh/agent.h"
 
-struct input_request {
-	enum oob_type type;
-	uint16_t len;
-	agent_input_cb cb;
+typedef enum {
+	MESH_AGENT_REQUEST_BLINK,
+	MESH_AGENT_REQUEST_BEEP,
+	MESH_AGENT_REQUEST_VIBRATE,
+	MESH_AGENT_REQUEST_OUT_NUMERIC,
+	MESH_AGENT_REQUEST_OUT_ALPHA,
+	MESH_AGENT_REQUEST_PUSH,
+	MESH_AGENT_REQUEST_TWIST,
+	MESH_AGENT_REQUEST_IN_NUMERIC,
+	MESH_AGENT_REQUEST_IN_ALPHA,
+	MESH_AGENT_REQUEST_STATIC_OOB,
+	MESH_AGENT_REQUEST_PRIVATE_KEY,
+	MESH_AGENT_REQUEST_PUBLIC_KEY
+} agent_request_type_t;
+
+struct agent_request {
+	agent_request_type_t type;
+	struct l_dbus_message *msg;
+	void *cb;
 	void *user_data;
 };
 
-static struct input_request pending_request = {NONE, 0, NULL, NULL};
+struct mesh_agent {
+	char *path;
+	char *owner;
+	struct mesh_agent_prov_caps caps;
+	struct agent_request *req;
+};
 
-bool agent_completion(void)
-{
-	if (pending_request.type == NONE)
-		return false;
+struct prov_action {
+	const char *action;
+	uint16_t output;
+	uint16_t input;
+	uint8_t size;
+};
+
+struct oob_info {
+	const char *oob;
+	uint16_t mask;
+};
 
-	return true;
+static struct prov_action cap_table[] = {
+	{"blink", 0x0001, 0x0000, 1},
+	{"beep", 0x0002, 0x0000, 1},
+	{"vibrate", 0x0004, 0x0000, 1},
+	{"out-numeric", 0x0008, 0x0000, 8},
+	{"out-alpha", 0x0010, 0x0000, 8},
+	{"push", 0x0000, 0x0001, 1},
+	{"twist", 0x0000, 0x0002, 1},
+	{"in-numeric", 0x0000, 0x0004, 8},
+	{"in-alpha", 0x0000, 0x0008, 8}
+};
+
+static struct oob_info oob_table[] = {
+	{"other", 0x0001},
+	{"uri", 0x0002},
+	{"machine-code-2d", 0x0004},
+	{"barcode", 0x0008},
+	{"nfc", 0x0010},
+	{"number", 0x0020},
+	{"string", 0x0040},
+	{"on-box", 0x0800},
+	{"in-box", 0x1000},
+	{"on-paper", 0x2000},
+	{"in-manual", 0x4000},
+	{"on-device", 0x8000}
+};
+
+static struct l_queue *agents;
+
+static bool simple_match(const void *a, const void *b)
+{
+	return a == b;
 }
 
-static void reset_input_request(void)
+static void parse_prov_caps(struct mesh_agent_prov_caps *caps,
+				struct l_dbus_message_iter *property)
 {
-	pending_request.type = NONE;
-	pending_request.len = 0;
-	pending_request.cb = NULL;
-	pending_request.user_data = NULL;
+	struct l_dbus_message_iter iter_caps;
+	const char *str;
+	uint32_t i;
+
+	if (!l_dbus_message_iter_get_variant(property, "as", &iter_caps))
+		return;
+
+	while (l_dbus_message_iter_next_entry(&iter_caps, &str)) {
+		for (i = 0; i < L_ARRAY_SIZE(cap_table); i++) {
+			if (strcmp(str, cap_table[i].action))
+				continue;
+
+			caps->output_action |= cap_table[i].output;
+			if (cap_table[i].output &&
+					caps->output_size < cap_table[i].size)
+				caps->output_size = cap_table[i].size;
+
+			caps->input_action |= cap_table[i].input;
+			if (cap_table[i].input &&
+					caps->input_size < cap_table[i].size)
+				caps->input_size = cap_table[i].size;
+
+			break;
+		}
+
+		if (!strcmp(str, "PublicOOB"))
+			caps->pub_type = 1;
+		else if (!strcmp(str, "StaticOOB"))
+			caps->static_type = 1;
+	}
+
 }
 
-static void try_again(void)
+static void parse_oob_info(struct mesh_agent_prov_caps *caps,
+				struct l_dbus_message_iter *property)
 {
-	static int try_count;
-	enum oob_type type = pending_request.type;
+	struct l_dbus_message_iter iter_oob;
+	uint32_t i;
+	const char *str;
 
-	if (try_count == 2) {
-		reset_input_request();
-		try_count = 0;
+	if (!l_dbus_message_iter_get_variant(property, "as", &iter_oob))
 		return;
+
+	while (l_dbus_message_iter_next_entry(&iter_oob, &str)) {
+		for (i = 0; i < L_ARRAY_SIZE(oob_table); i++) {
+			if (strcmp(str, oob_table[i].oob))
+				continue;
+			caps->oob_info |= oob_table[i].mask;
+		}
 	}
+}
 
-	pending_request.type = NONE;
-	agent_input_request(type, pending_request.len, pending_request.cb,
-						pending_request.user_data);
+static void agent_free(void *agent_data)
+{
+	struct mesh_agent *agent = agent_data;
+	int err;
+	mesh_agent_cb_t simple_cb;
+	mesh_agent_key_cb_t key_cb;
+	mesh_agent_number_cb_t number_cb;
 
-	try_count++;
+	if (!l_queue_find(agents, simple_match, agent))
+		return;
+
+	err = MESH_ERROR_DOES_NOT_EXIST;
+
+	if (agent->req && agent->req->cb) {
+		struct agent_request *req = agent->req;
+
+		switch (req->type) {
+		case MESH_AGENT_REQUEST_PUSH:
+		case MESH_AGENT_REQUEST_TWIST:
+		case MESH_AGENT_REQUEST_IN_NUMERIC:
+			number_cb = req->cb;
+			number_cb(req->user_data, err, 0);
+			break;
+		case MESH_AGENT_REQUEST_IN_ALPHA:
+		case MESH_AGENT_REQUEST_STATIC_OOB:
+		case MESH_AGENT_REQUEST_PRIVATE_KEY:
+		case MESH_AGENT_REQUEST_PUBLIC_KEY:
+			key_cb = req->cb;
+			key_cb(req->user_data, err, NULL, 0);
+			break;
+		case MESH_AGENT_REQUEST_BLINK:
+		case MESH_AGENT_REQUEST_BEEP:
+		case MESH_AGENT_REQUEST_VIBRATE:
+		case MESH_AGENT_REQUEST_OUT_NUMERIC:
+		case MESH_AGENT_REQUEST_OUT_ALPHA:
+			simple_cb = agent->req->cb;
+			simple_cb(req->user_data, err);
+		default:
+			break;
+		}
+
+		l_dbus_message_unref(req->msg);
+		l_free(req);
+	}
+
+	l_free(agent->path);
+	l_free(agent->owner);
 }
 
-static void response_hexadecimal(const char *input, void *user_data)
+void mesh_agent_remove(struct mesh_agent *agent)
 {
-	uint8_t buf[MAX_HEXADECIMAL_OOB_LEN];
+	if (!l_queue_find(agents, simple_match, agent))
+		return;
+
+	agent_free(agent);
+	l_queue_remove(agents, agent);
+}
 
-	if (!str2hex(input, strlen(input), buf, pending_request.len)) {
-		bt_shell_printf("Incorrect input: expecting %d hex octets\n",
-							pending_request.len);
-		try_again();
+void mesh_agent_cleanup(void)
+{
+	if (!agents)
 		return;
+
+	l_queue_destroy(agents, agent_free);
+
+}
+
+void mesh_agent_init(void)
+{
+	if (!agents)
+		agents = l_queue_new();
+}
+
+struct mesh_agent *mesh_agent_create(const char *path, const char *owner,
+					struct l_dbus_message_iter *properties)
+{
+	struct mesh_agent *agent;
+	const char *key, *uri_string;
+	struct l_dbus_message_iter variant;
+
+	agent = l_new(struct mesh_agent, 1);
+
+	while (l_dbus_message_iter_next_entry(properties, &key, &variant)) {
+		if (!strcmp(key, "Capabilities")) {
+			parse_prov_caps(&agent->caps, &variant);
+		} else if (!strcmp(key, "URI")) {
+			l_dbus_message_iter_get_variant(&variant, "s",
+								&uri_string);
+			/* TODO: compute hash */
+		} else if (!strcmp(key, "OutOfBandInfo")) {
+			parse_oob_info(&agent->caps, &variant);
+		}
+	}
+
+	agent->owner = l_strdup(owner);
+	agent->path = l_strdup(path);
+
+	l_queue_push_tail(agents, agent);
+
+	return agent;
+}
+
+struct mesh_agent_prov_caps *mesh_agent_get_caps(struct mesh_agent *agent)
+{
+	if (!agent || !l_queue_find(agents, simple_match, agent))
+		return NULL;
+
+	return &agent->caps;
+}
+
+static struct agent_request *create_request(agent_request_type_t type,
+						void *cb, void *data)
+{
+	struct agent_request *req;
+
+	req = l_new(struct agent_request, 1);
+
+	req->type = type;
+	req->cb = cb;
+	req->user_data = data;
+
+	return req;
+}
+
+static int get_reply_error(struct l_dbus_message *reply)
+{
+	const char *name, *desc;
+
+	if (l_dbus_message_is_error(reply)) {
+
+		l_dbus_message_get_error(reply, &name, &desc);
+		l_error("Agent failed output action (%s), %s", name, desc);
+		return MESH_ERROR_FAILED;
 	}
 
-	if (pending_request.cb)
-		pending_request.cb(HEXADECIMAL, buf, pending_request.len,
-					pending_request.user_data);
+	return MESH_ERROR_NONE;
+}
+
+static void simple_reply(struct l_dbus_message *reply, void *user_data)
+{
+	struct mesh_agent *agent = user_data;
+	struct agent_request *req;
+	mesh_agent_cb_t cb;
+	int err;
+
+	if (!l_queue_find(agents, simple_match, agent) || !agent->req)
+		return;
+
+	req = agent->req;
+
+	err = get_reply_error(reply);
 
-	reset_input_request();
+	l_dbus_message_unref(req->msg);
+
+	if (req->cb) {
+		cb = req->cb;
+		cb(req->user_data, err);
+	}
+
+	l_free(req);
+	agent->req = NULL;
 }
 
-static void response_decimal(const char *input, void *user_data)
+static void numeric_reply(struct l_dbus_message *reply, void *user_data)
 {
-	uint8_t buf[DECIMAL_OOB_LEN];
+	struct mesh_agent *agent = user_data;
+	struct agent_request *req;
+	mesh_agent_number_cb_t cb;
+	uint32_t count;
+	int err;
 
-	if (strlen(input) > pending_request.len) {
-		bt_shell_printf("Bad input: expected no more than %d digits\n",
-						pending_request.len);
-		try_again();
+	if (!l_queue_find(agents, simple_match, agent) || !agent->req)
 		return;
+
+	req = agent->req;
+
+	err = get_reply_error(reply);
+
+	count = 0;
+
+	if (err == MESH_ERROR_NONE) {
+		if (!l_dbus_message_get_arguments(reply, "u", &count)) {
+			l_error("Failed to retrieve numeric input");
+			err = MESH_ERROR_FAILED;
+		}
+	}
+
+	l_dbus_message_unref(req->msg);
+
+	if (req->cb) {
+		cb = req->cb;
+		cb(req->user_data, err, count);
+	}
+
+	l_free(req);
+	agent->req = NULL;
+}
+
+static void key_reply(struct l_dbus_message *reply, void *user_data)
+{
+	struct mesh_agent *agent = user_data;
+	struct agent_request *req;
+	mesh_agent_key_cb_t cb;
+	struct l_dbus_message_iter iter_array;
+	uint32_t n = 0, expected_len = 0;
+	uint8_t buf[64];
+	int err;
+
+	if (!l_queue_find(agents, simple_match, agent) || !agent->req)
+		return;
+
+	req = agent->req;
+
+	err = get_reply_error(reply);
+
+	if (err != MESH_ERROR_NONE)
+		goto done;
+
+	if (!l_dbus_message_get_arguments(reply, "au", &iter_array)) {
+		l_error("Failed to retrieve key input");
+		err = MESH_ERROR_FAILED;
+		goto done;
 	}
 
-	l_put_be32(atoi(input), buf);
+	if (!l_dbus_message_iter_get_fixed_array(&iter_array, buf, &n)) {
+		l_error("Failed to retrieve key input");
+		err = MESH_ERROR_FAILED;
+		goto done;
+	}
 
-	if (pending_request.cb)
-		pending_request.cb(DECIMAL, buf, DECIMAL_OOB_LEN,
-					pending_request.user_data);
+	if (req->type == MESH_AGENT_REQUEST_PRIVATE_KEY)
+		expected_len = 32;
+	else if (MESH_AGENT_REQUEST_PUBLIC_KEY)
+		expected_len = 64;
+	else
+		expected_len = 16;
+
+	if (n != expected_len) {
+		l_error("Bad response length: %u (need %u)", n, expected_len);
+		err = MESH_ERROR_FAILED;
+		n = 0;
+	}
 
-	reset_input_request();
+done:
+	l_dbus_message_unref(req->msg);
+
+	if (req->cb) {
+		cb = req->cb;
+		cb(req->user_data, err, buf, n);
+	}
+
+	l_free(req);
+	agent->req = NULL;
 }
 
-static void response_ascii(const char *input, void *user_data)
+static int output_request(struct mesh_agent *agent, const char *action,
+					agent_request_type_t type, uint32_t cnt,
+					void *cb, void *user_data)
 {
-	if (pending_request.cb)
-		pending_request.cb(ASCII, (uint8_t *) input, strlen(input),
-					pending_request.user_data);
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+	struct l_dbus_message_builder *builder;
+
+	if (!l_queue_find(agents, simple_match, agent))
+		return MESH_ERROR_DOES_NOT_EXIST;
 
-	reset_input_request();
+	if (agent->req)
+		return MESH_ERROR_BUSY;
+
+	agent->req = create_request(type, cb, user_data);
+	msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path,
+						MESH_PROVISION_AGENT_INTERFACE,
+						"DisplayNumeric");
+
+	builder = l_dbus_message_builder_new(msg);
+	l_dbus_message_builder_append_basic(builder, 's', action);
+	l_dbus_message_builder_append_basic(builder, 'u', &cnt);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+
+	l_debug("Send DisplayNumeric request to %s %s",
+						agent->owner, agent->path);
+
+	l_dbus_send_with_reply(dbus_get_bus(), msg, simple_reply, agent,
+									NULL);
+
+	agent->req->msg = l_dbus_message_ref(msg);
+
+	return MESH_ERROR_NONE;
 }
 
-static bool request_hexadecimal(uint16_t len)
+static int prompt_input(struct mesh_agent *agent, const char *action,
+					agent_request_type_t type, bool numeric,
+					void *cb, void *user_data)
 {
-	if (len > MAX_HEXADECIMAL_OOB_LEN)
-		return false;
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+	struct l_dbus_message_builder *builder;
+	const char *method_name;
+	l_dbus_message_func_t reply_cb;
+
+	if (!l_queue_find(agents, simple_match, agent))
+		return MESH_ERROR_DOES_NOT_EXIST;
 
-	bt_shell_printf("Request hexadecimal key (hex %d octets)\n", len);
-	bt_shell_prompt_input("mesh", "Enter key (hex number):",
-						response_hexadecimal, NULL);
+	if (agent->req)
+		return MESH_ERROR_BUSY;
 
-	return true;
+	agent->req = create_request(type, cb, user_data);
+
+	method_name = numeric ? "PromptNumeric" : "PromptStatic";
+
+	msg = l_dbus_message_new_method_call(dbus, agent->owner,
+						agent->path,
+						MESH_PROVISION_AGENT_INTERFACE,
+						method_name);
+
+	builder = l_dbus_message_builder_new(msg);
+	l_dbus_message_builder_append_basic(builder, 's', action);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+
+	l_debug("Send \"%s\" input request to %s %s", action,
+						agent->owner, agent->path);
+
+	reply_cb = numeric ? numeric_reply : key_reply;
+
+	l_dbus_send_with_reply(dbus_get_bus(), msg, reply_cb, agent, NULL);
+
+	agent->req->msg = l_dbus_message_ref(msg);
+
+	return MESH_ERROR_NONE;
 }
 
-static uint32_t power_ten(uint8_t power)
+static int request_key(struct mesh_agent *agent,
+					agent_request_type_t type,
+					void *cb, void *user_data)
 {
-	uint32_t ret = 1;
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+	const char *method_name;
+
+	if (!l_queue_find(agents, simple_match, agent))
+		return MESH_ERROR_DOES_NOT_EXIST;
 
-	while (power--)
-		ret *= 10;
+	if (agent->req)
+		return MESH_ERROR_BUSY;
 
-	return ret;
+	agent->req = create_request(type, cb, user_data);
+
+	method_name = (type == MESH_AGENT_REQUEST_PRIVATE_KEY) ?
+						"PrivateKey" : "PublicKey";
+
+	msg = l_dbus_message_new_method_call(dbus, agent->owner,
+						agent->path,
+						MESH_PROVISION_AGENT_INTERFACE,
+						method_name);
+
+	l_debug("Send key request to %s %s", agent->owner, agent->path);
+
+	l_dbus_send_with_reply(dbus_get_bus(), msg, key_reply, agent, NULL);
+
+	agent->req->msg = l_dbus_message_ref(msg);
+
+	return MESH_ERROR_NONE;
 }
 
-static bool request_decimal(uint16_t len)
+int mesh_agent_display_string(struct mesh_agent *agent, const char *str,
+				mesh_agent_cb_t cb, void *user_data)
 {
-	bt_shell_printf("Request decimal key (0 - %d)\n", power_ten(len) - 1);
-	bt_shell_prompt_input("mesh", "Enter Numeric key:", response_decimal,
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+	struct l_dbus_message_builder *builder;
+
+	if (!l_queue_find(agents, simple_match, agent))
+		return MESH_ERROR_DOES_NOT_EXIST;
+
+	if (agent->req)
+		return MESH_ERROR_BUSY;
+
+	agent->req = create_request(MESH_AGENT_REQUEST_OUT_ALPHA,
+								cb, user_data);
+	msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path,
+						MESH_PROVISION_AGENT_INTERFACE,
+						"DisplayString");
+
+	builder = l_dbus_message_builder_new(msg);
+	l_dbus_message_builder_append_basic(builder, 's', str);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+
+	l_debug("Send DisplayString request to %s %s",
+						agent->owner, agent->path);
+
+	l_dbus_send_with_reply(dbus_get_bus(), msg, simple_reply, agent,
 									NULL);
 
-	return true;
+	agent->req->msg = l_dbus_message_ref(msg);
+
+	return MESH_ERROR_NONE;
+
 }
 
-static bool request_ascii(uint16_t len)
+int mesh_agent_display_number(struct mesh_agent *agent, bool initiator,
+					uint8_t action, uint32_t count,
+					mesh_agent_cb_t cb, void *user_data)
 {
-	if (len > MAX_ASCII_OOB_LEN)
-		return false;
+	const char *str_type;
+	agent_request_type_t type;
+
+	type = action;
+
+	if (initiator)
+		type = action + MESH_AGENT_REQUEST_PUSH;
+
+	if (type >= L_ARRAY_SIZE(cap_table))
+		return MESH_ERROR_INVALID_ARGS;
 
-	bt_shell_printf("Request ASCII key (max characters %d)\n", len);
-	bt_shell_prompt_input("mesh", "Enter key (ascii string):",
-							response_ascii, NULL);
+	str_type = cap_table[type].action;
 
-	return true;
+	return output_request(agent, str_type, type, count, cb, user_data);
 }
 
-bool agent_input_request(enum oob_type type, uint16_t max_len,
-					agent_input_cb cb, void *user_data)
+int mesh_agent_prompt_number(struct mesh_agent *agent, bool initiator,
+						uint8_t action,
+						mesh_agent_number_cb_t cb,
+						void *user_data)
 {
-	bool result;
+	const char *str_type;
+	agent_request_type_t type;
 
-	if (pending_request.type != NONE)
-		return false;
+	type = action;
 
-	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 (!initiator)
+		type = action + MESH_AGENT_REQUEST_PUSH;
 
-	if (result) {
-		pending_request.type = type;
-		pending_request.len = max_len;
-		pending_request.cb = cb;
-		pending_request.user_data = user_data;
+	if (type >= L_ARRAY_SIZE(cap_table))
+		return MESH_ERROR_INVALID_ARGS;
 
-		return true;
-	}
+	str_type = cap_table[type].action;
+
+	return prompt_input(agent, str_type, type, true, cb, user_data);
+}
 
-	return false;
+int mesh_agent_prompt_alpha(struct mesh_agent *agent, mesh_agent_key_cb_t cb,
+								void *user_data)
+{
+	return prompt_input(agent, "in-alpha", MESH_AGENT_REQUEST_IN_ALPHA,
+							false, cb, user_data);
 }
 
-static void response_output(const char *input, void *user_data)
+int mesh_agent_request_static(struct mesh_agent *agent, mesh_agent_key_cb_t cb,
+								void *user_data)
 {
-	reset_input_request();
+	return prompt_input(agent, "static-oob", MESH_AGENT_REQUEST_STATIC_OOB,
+							false, cb, user_data);
 }
 
-bool agent_output_request(const char *str)
+int mesh_agent_request_private_key(struct mesh_agent *agent,
+				mesh_agent_key_cb_t cb, void *user_data)
 {
-	if (pending_request.type != NONE)
-		return false;
+	return request_key(agent, MESH_AGENT_REQUEST_PRIVATE_KEY, cb,
+								user_data);
 
-	pending_request.type = OUTPUT;
-	bt_shell_prompt_input("mesh", str, response_output, NULL);
-	return true;
 }
 
-void agent_output_request_cancel(void)
+int mesh_agent_request_public_key(struct mesh_agent *agent,
+				mesh_agent_key_cb_t cb, void *user_data)
 {
-	if (pending_request.type != OUTPUT)
+	return request_key(agent, MESH_AGENT_REQUEST_PUBLIC_KEY, cb,
+								user_data);
+}
+
+void mesh_agent_cancel(struct mesh_agent *agent)
+{
+	struct l_dbus *dbus = dbus_get_bus();
+	struct l_dbus_message *msg;
+
+	if (!l_queue_find(agents, simple_match, agent))
 		return;
 
-	pending_request.type = NONE;
-	bt_shell_release_prompt("");
+	msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path,
+						MESH_PROVISION_AGENT_INTERFACE,
+						"Cancel");
+	l_dbus_send(dbus, msg);
 }
diff --git a/mesh/agent.h b/mesh/agent.h
index 6fb475691..0a499d2d5 100644
--- a/mesh/agent.h
+++ b/mesh/agent.h
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2017  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2018  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -15,28 +15,55 @@
  *  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
+struct mesh_agent;
+
+struct mesh_agent_prov_caps {
+	uint32_t uri_hash;
+	uint16_t oob_info;
+	uint16_t output_action;
+	uint16_t input_action;
+	uint8_t pub_type;
+	uint8_t static_type;
+	uint8_t output_size;
+	uint8_t input_size;
+};
+
+typedef void (*mesh_agent_cb_t) (void *user_data, int err);
+
+typedef void (*mesh_agent_key_cb_t) (void *user_data, int err, uint8_t *key,
+								uint32_t len);
+
+typedef void (*mesh_agent_number_cb_t) (void *user_data, int err,
+							uint32_t number);
+
+void mesh_agent_init(void);
+void mesh_agent_cleanup(void);
+struct mesh_agent *mesh_agent_create(const char *path, const char *owner,
+					struct l_dbus_message_iter *properties);
 
-enum oob_type {
-	NONE,
-	HEXADECIMAL,
-	DECIMAL,
-	ASCII,
-	OUTPUT,
-} oob_type_t;
+void mesh_agent_remove(struct mesh_agent *agent);
+void mesh_agent_cancel(struct mesh_agent *agent);
 
-typedef void (*agent_input_cb)(enum oob_type type, void *input, uint16_t len,
+struct mesh_agent_prov_caps *mesh_agent_get_caps(struct mesh_agent *agent);
+
+int mesh_agent_display_number(struct mesh_agent *agent, bool initiator,
+					uint8_t action, uint32_t count,
+					mesh_agent_cb_t cb, void *user_data);
+int mesh_agent_prompt_number(struct mesh_agent *agent, bool initiator,
+				uint8_t action, mesh_agent_number_cb_t cb,
+				void *user_data);
+int mesh_agent_prompt_alpha(struct mesh_agent *agent, mesh_agent_key_cb_t cb,
+							void *user_data);
+int mesh_agent_request_static(struct mesh_agent *agent, mesh_agent_key_cb_t cb,
+							void *user_data);
+int mesh_agent_request_private_key(struct mesh_agent *agent,
+							mesh_agent_key_cb_t cb,
+							void *user_data);
+int mesh_agent_request_public_key(struct mesh_agent *agent,
+							mesh_agent_key_cb_t cb,
+							void *user_data);
+int mesh_agent_display_string(struct mesh_agent *agent, const char *str,
+							mesh_agent_cb_t cb,
 							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);
-- 
2.14.5


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

* [PATCH BlueZ v6 20/26] mesh: restructure App Key storage
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (18 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 19/26] mesh: DBUS interface for Provisioning Agent Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 21/26] mesh: Clean-up Comment style Brian Gix
                   ` (5 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

Implements App key specific processing including Replay Protection
and Key Updating.
---
 mesh/appkey.c | 19 ++++++++++---------
 mesh/appkey.h |  1 -
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/mesh/appkey.c b/mesh/appkey.c
index 3d445d217..a437763db 100644
--- a/mesh/appkey.c
+++ b/mesh/appkey.c
@@ -15,7 +15,6 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -24,6 +23,7 @@
 
 #define _GNU_SOURCE
 #include <ell/ell.h>
+#include <json-c/json.h>
 
 #include "mesh/mesh-defs.h"
 
@@ -31,7 +31,7 @@
 #include "mesh/node.h"
 #include "mesh/net.h"
 #include "mesh/crypto.h"
-#include "mesh/display.h"
+#include "mesh/util.h"
 #include "mesh/model.h"
 #include "mesh/storage.h"
 #include "mesh/appkey.h"
@@ -206,12 +206,12 @@ bool appkey_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
 		}
 
 		if (seq < msg->seq) {
-			l_info("Ignoring packet with lower sequence number");
+			l_debug("Ignoring packet with lower sequence number");
 			return true;
 		}
 
 		if (seq == msg->seq) {
-			l_info("Message already processed (duplicate)");
+			l_debug("Message already processed (duplicate)");
 			return true;
 		}
 
@@ -302,6 +302,7 @@ bool appkey_key_init(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
 		return false;
 
 	key->net_idx = net_idx;
+	key->app_idx = app_idx;
 
 	if (key_value && !set_key(key, app_idx, key_value, false))
 		return false;
@@ -392,14 +393,14 @@ int appkey_key_add(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
 			return MESH_STATUS_SUCCESS;
 
 		if (!update) {
-			l_info("Failed to add key: index already stored %x",
+			l_debug("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)
+		if (!(l_queue_length(app_keys) < MAX_APP_KEYS))
 			return MESH_STATUS_INSUFF_RESOURCES;
 
 		key = app_key_new();
@@ -411,7 +412,7 @@ int appkey_key_add(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
 			return MESH_STATUS_INSUFF_RESOURCES;
 		}
 
-		if (!storage_local_app_key_add(net, net_idx, app_idx, new_key,
+		if (!storage_app_key_add(net, net_idx, app_idx, new_key,
 								false)) {
 			appkey_key_free(key);
 			return MESH_STATUS_STORAGE_FAIL;
@@ -424,7 +425,7 @@ int appkey_key_add(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
 		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,
+		if (!storage_app_key_add(net, net_idx, app_idx, new_key,
 								true))
 			return MESH_STATUS_STORAGE_FAIL;
 	}
@@ -457,7 +458,7 @@ int appkey_key_delete(struct mesh_net *net, uint16_t net_idx,
 	l_queue_remove(app_keys, key);
 	appkey_key_free(key);
 
-	if (!storage_local_app_key_del(net, net_idx, app_idx))
+	if (!storage_app_key_del(net, net_idx, app_idx))
 		return MESH_STATUS_STORAGE_FAIL;
 
 	return MESH_STATUS_SUCCESS;
diff --git a/mesh/appkey.h b/mesh/appkey.h
index 8fce3dcd0..21bc6a70e 100644
--- a/mesh/appkey.h
+++ b/mesh/appkey.h
@@ -15,7 +15,6 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 /* TODO: get this number from configuration */
-- 
2.14.5


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

* [PATCH BlueZ v6 21/26] mesh: Clean-up Comment style
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (19 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 20/26] mesh: restructure App Key storage Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 22/26] mesh: Update for DBus API and multi-node support Brian Gix
                   ` (4 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

---
 mesh/friend.c | 76 +++++++++++++++++++++++++++++------------------------------
 1 file changed, 38 insertions(+), 38 deletions(-)

diff --git a/mesh/friend.c b/mesh/friend.c
index 9ce499463..d17fde084 100644
--- a/mesh/friend.c
+++ b/mesh/friend.c
@@ -34,7 +34,7 @@
 #include "mesh/net.h"
 #include "mesh/crypto.h"
 #include "mesh/model.h"
-#include "mesh/display.h"
+#include "mesh/util.h"
 
 #include "mesh/friend.h"
 
@@ -78,7 +78,7 @@ static void response_timeout(struct l_timeout *timeout, void *user_data)
 	struct frnd_negotiation *neg = user_data;
 
 	/* LPN did not choose us */
-	l_info("Did not win negotiation for %4.4x", neg->low_power_node);
+	l_debug("Did not win negotiation for %4.4x", neg->low_power_node);
 
 	net_key_unref(neg->key_id);
 	l_queue_remove(frnd_negotiations, neg);
@@ -177,13 +177,13 @@ void friend_request(struct mesh_net *net, uint16_t src,
 	uint8_t minCache = (minReq >> 0) & 7;
 	int32_t rsp_delay;
 
-	l_info("RSSI of Request: %d dbm", rssi);
-	l_info("Delay: %d ms", delay);
-	l_info("Poll Timeout of Request: %d ms", timeout * 100);
-	l_info("Previous Friend: %4.4x", prev);
-	l_info("Num Elem: %2.2x", num_ele);
-	l_info("Cache Requested: %d", cache_size(minCache));
-	l_info("Cache to offer: %d", frnd_cache_size);
+	l_debug("RSSI of Request: %d dbm", rssi);
+	l_debug("Delay: %d ms", delay);
+	l_debug("Poll Timeout of Request: %d ms", timeout * 100);
+	l_debug("Previous Friend: %4.4x", prev);
+	l_debug("Num Elem: %2.2x", num_ele);
+	l_debug("Cache Requested: %d", cache_size(minCache));
+	l_debug("Cache to offer: %d", frnd_cache_size);
 
 	/* Determine our own suitability before
 	 * deciding to participate in negotiation
@@ -224,7 +224,7 @@ void friend_request(struct mesh_net *net, uint16_t src,
 	 * of 1, bit zero and additional 0.5
 	 */
 	rsp_delay = -(rssi * scaling[rssiScale]);
-	l_info("RSSI Factor: %d ms", rsp_delay / 10);
+	l_debug("RSSI Factor: %d ms", rsp_delay / 10);
 
 	/* Relay Window (Positive Factor, larger values == more time)
 	 * Scaling factor 0-3 == multiplier of 1.0 - 2.5
@@ -232,7 +232,7 @@ void friend_request(struct mesh_net *net, uint16_t src,
 	 * of 1, bit zero and additional 0.5
 	 */
 	rsp_delay += frnd_relay_window * scaling[winScale];
-	l_info("Win Size Factor: %d ms",
+	l_debug("Win Size Factor: %d ms",
 			(frnd_relay_window * scaling[winScale]) / 10);
 
 	/* Normalize to ms */
@@ -244,7 +244,7 @@ void friend_request(struct mesh_net *net, uint16_t src,
 	else if (rsp_delay > MAX_RESP_DELAY)
 		rsp_delay = MAX_RESP_DELAY;
 
-	l_info("Total Response Delay: %d ms", rsp_delay);
+	l_debug("Total Response Delay: %d ms", rsp_delay);
 
 	/* Add in 100ms delay before start of "Offer Period" */
 	rsp_delay += RESPONSE_DELAY;
@@ -261,7 +261,7 @@ void friend_clear_confirm(struct mesh_net *net, uint16_t src,
 	struct frnd_negotiation *neg = l_queue_remove_if(frnd_negotiations,
 					match_by_lpn, L_UINT_TO_PTR(lpn));
 
-	l_info("Friend Clear confirmed %4.4x (cnt %4.4x)", lpn, lpnCounter);
+	l_debug("Friend Clear confirmed %4.4x (cnt %4.4x)", lpn, lpnCounter);
 
 	if (!neg)
 		return;
@@ -276,7 +276,7 @@ static void friend_poll_timeout(struct l_timeout *timeout, void *user_data)
 	struct mesh_friend *frnd = user_data;
 
 	if (mesh_friend_clear(frnd->net, frnd))
-		l_info("Friend Poll Timeout %4.4x", frnd->dst);
+		l_debug("Friend Poll Timeout %4.4x", frnd->dst);
 
 	l_timeout_remove(frnd->timeout);
 	frnd->timeout = NULL;
@@ -343,7 +343,7 @@ void friend_clear(struct mesh_net *net, uint16_t src, uint16_t lpn,
 			return;
 	}
 
-	l_info("Friend Cleared %4.4x (%4.4x)", lpn, lpnCounter);
+	l_debug("Friend Cleared %4.4x (%4.4x)", lpn, lpnCounter);
 
 	l_put_be16(lpn, msg + 1);
 	l_put_be16(lpnCounter, msg + 3);
@@ -369,11 +369,11 @@ static void clear_retry(struct l_timeout *timeout, void *user_data)
 
 	if (secs && ((secs << 1) < neg->poll_timeout/10)) {
 		neg->receive_delay++;
-		l_info("Try FRND_CLR again in %d seconds (total timeout %d)",
+		l_debug("Try FRND_CLR again in %d seconds (total timeout %d)",
 						secs, neg->poll_timeout/10);
 		l_timeout_modify(neg->timeout, secs);
 	} else {
-		l_info("FRND_CLR timed out %d", secs);
+		l_debug("FRND_CLR timed out %d", secs);
 		l_timeout_remove(timeout);
 		l_queue_remove(frnd_negotiations, neg);
 		l_free(neg);
@@ -407,7 +407,7 @@ static void friend_delay_rsp(struct l_timeout *timeout, void *user_data)
 
 			seqZero &= SEQ_ZERO_MASK;
 
-			l_info("Fwd ACK pkt %6.6x-%8.8x",
+			l_debug("Fwd ACK pkt %6.6x-%8.8x",
 					pkt->u.one[0].seq,
 					pkt->iv_index);
 
@@ -420,7 +420,7 @@ static void friend_delay_rsp(struct l_timeout *timeout, void *user_data)
 
 
 		} else {
-			l_info("Fwd CTL pkt %6.6x-%8.8x",
+			l_debug("Fwd CTL pkt %6.6x-%8.8x",
 					pkt->u.one[0].seq,
 					pkt->iv_index);
 
@@ -442,7 +442,7 @@ static void friend_delay_rsp(struct l_timeout *timeout, void *user_data)
 		else
 			len = pkt->last_len;
 
-		l_info("Fwd FRND pkt %6.6x",
+		l_debug("Fwd FRND pkt %6.6x",
 				pkt->u.s12[pkt->cnt_out].seq);
 
 		print_packet("Frnd-Msg", pkt->u.s12[pkt->cnt_out].data, len);
@@ -462,7 +462,7 @@ static void friend_delay_rsp(struct l_timeout *timeout, void *user_data)
 update:
 	/* No More Data -- send Update message with md = false */
 	net_seq = mesh_net_get_seq_num(net);
-	l_info("Fwd FRND UPDATE %6.6x with MD == 0", net_seq);
+	l_debug("Fwd FRND UPDATE %6.6x with MD == 0", net_seq);
 
 	frnd->last = frnd->seq;
 	mesh_net_get_snb_state(net, upd + 1, &iv_index);
@@ -488,7 +488,7 @@ void friend_poll(struct mesh_net *net, uint16_t src, bool seq,
 	if (neg && !neg->clearing) {
 		uint8_t msg[5] = { NET_OP_FRND_CLEAR };
 
-		l_info("Won negotiation for %4.4x", neg->low_power_node);
+		l_debug("Won negotiation for %4.4x", neg->low_power_node);
 
 		/* This call will clean-up and replace if already friends */
 		frnd = mesh_friend_new(net, src, neg->num_ele,
@@ -695,7 +695,7 @@ void frnd_offer(struct mesh_net *net, uint16_t src, uint8_t window,
 {
 	struct frnd_offers *offer;
 
-	l_info("RSSI of Offer: %d dbm", l_rssi);
+	l_debug("RSSI of Offer: %d dbm", l_rssi);
 
 	/* Ignore RFU window value 0 */
 	if (window == 0)
@@ -735,7 +735,7 @@ static void frnd_negotiated_to(struct l_timeout *timeout, void *user_data)
 {
 	struct mesh_net *net = user_data;
 
-	l_info("frnd_negotiated_to");
+	l_debug("frnd_negotiated_to");
 	if (!mesh_net_get_friend(net)) {
 		l_timeout_remove(poll_period_to);
 		poll_period_to = NULL;
@@ -776,7 +776,7 @@ void frnd_poll(struct mesh_net *net, bool retry)
 		seq = !seq;
 		mesh_net_set_frnd_seq(net, seq);
 	} else if (!(poll_cnt--)) {
-		l_info("Lost Friendship with %4.4x", old_friend);
+		l_debug("Lost Friendship with %4.4x", old_friend);
 		l_timeout_remove(poll_period_to);
 		poll_period_to = NULL;
 		frnd_poll_cancel(net);
@@ -790,7 +790,7 @@ void frnd_poll(struct mesh_net *net, bool retry)
 	if (poll_retry_to)
 		l_timeout_remove(poll_retry_to);
 
-	l_info("TX-FRIEND POLL %d", seq);
+	l_debug("TX-FRIEND POLL %d", seq);
 	msg[1] = seq;
 	net_seq = mesh_net_get_seq_num(net);
 	mesh_net_transport_send(net, key_id, true,
@@ -858,7 +858,7 @@ static void req_timeout(struct l_timeout *timeout, void *user_data)
 		l_free(best);
 		return;
 	} else if (!best) {
-		l_info("No Offers Received");
+		l_debug("No Offers Received");
 		return;
 	}
 
@@ -884,7 +884,7 @@ static void req_timeout(struct l_timeout *timeout, void *user_data)
 
 old_keys_only:
 
-	l_info("Winning offer %4.4x RSSI: %ddb Window: %dms Cache sz: %d",
+	l_debug("Winning offer %4.4x RSSI: %ddb Window: %dms Cache sz: %d",
 			best->src, best->local_rssi,
 			best->window, best->cache);
 
@@ -930,23 +930,23 @@ void frnd_request_friend(struct mesh_net *net, uint8_t cache,
 		offers = l_queue_new();
 
 	msg[n++] = NET_OP_FRND_REQUEST;
-	msg[n] = cache & 0x07;		// MinRequirements - Cache
-	msg[n++] |= (offer_delay & 0x0f) << 3;	// Offer Delay
-	poll_period_ms = (timeout * 300) / 4; // 3/4 of the time in ms
-	l_put_be32(timeout, msg + n);	// PollTimeout
-	msg[n++] = delay;		// ReceiveDelay
+	msg[n] = cache & 0x07;		/* MinRequirements - Cache */
+	msg[n++] |= (offer_delay & 0x0f) << 3;	/* Offer Delay */
+	poll_period_ms = (timeout * 300) / 4; /* 3/4 of the time in ms */
+	l_put_be32(timeout, msg + n);	/* PollTimeout */
+	msg[n++] = delay;		/* ReceiveDelay */
 	n += 3;
-	l_put_be16(old_friend, msg + n);	// PreviousAddress
+	l_put_be16(old_friend, msg + n);	/* PreviousAddress */
 	n += 2;
-	msg[n++] = mesh_net_get_num_ele(net);	// NumElements
-	l_put_be16(cnt + 1, msg + n);	// Next counter
+	msg[n++] = mesh_net_get_num_ele(net);	/* NumElements */
+	l_put_be16(cnt + 1, msg + n);	/* Next counter */
 	n += 2;
 	print_packet("Tx-NET_OP_FRND_REQUEST", msg, n);
 	mesh_net_transport_send(net, 0, false,
 			mesh_net_get_iv_index(net), 0,
 			0, 0, FRIENDS_ADDRESS,
 			msg, n);
-	l_timeout_create_ms(1000, req_timeout, net, NULL); // 1000 ms
+	l_timeout_create_ms(1000, req_timeout, net, NULL); /* 1000 ms */
 	mesh_net_set_friend(net, 0);
 	cnt++;
 }
@@ -1036,7 +1036,7 @@ void frnd_key_refresh(struct mesh_net *net, uint8_t phase)
 	case 0:
 	case 3:
 		if (new_lpn_id) {
-			l_info("LPN Retiring KeySet %d", lpn_key_id);
+			l_debug("LPN Retiring KeySet %d", lpn_key_id);
 			net_key_unref(lpn_key_id);
 			lpn_key_id = new_lpn_id;
 		}
-- 
2.14.5


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

* [PATCH BlueZ v6 22/26] mesh: Update for DBus API and multi-node support
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (20 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 21/26] mesh: Clean-up Comment style Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 23/26] mesh: Add default location for Mesh Node storage Brian Gix
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

Main function with high level plumbing for incoming D-Bus
messaging, and optional control of storage locations.
---
 mesh/main.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 67 insertions(+), 8 deletions(-)

diff --git a/mesh/main.c b/mesh/main.c
index 173f57a8f..93a646895 100644
--- a/mesh/main.c
+++ b/mesh/main.c
@@ -15,7 +15,6 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
  *
- *
  */
 
 #ifdef HAVE_CONFIG_H
@@ -33,11 +32,15 @@
 #include <sys/stat.h>
 #include <ell/ell.h>
 
+#include <dbus/dbus.h>
+#include <json-c/json.h>
+
 #include "lib/bluetooth.h"
 #include "lib/mgmt.h"
 
 #include "mesh/mesh.h"
 #include "mesh/net.h"
+#include "mesh/dbus.h"
 #include "mesh/storage.h"
 
 static const struct option main_options[] = {
@@ -56,12 +59,46 @@ static void usage(void)
 	       "\tmeshd [options]\n");
 	l_info("Options:\n"
 	       "\t--index <hcinum>  Use specified controller\n"
-	       "\t--config          Configuration file\n"
+	       "\t--config          Configuration directory\n"
 	       "\t--nodetach        Run in foreground\n"
 	       "\t--debug           Enable debug output\n"
 	       "\t--help            Show %s information\n", __func__);
 }
 
+static void do_debug(const char *str, void *user_data)
+{
+	const char *prefix = user_data;
+
+	l_info("%s%s", prefix, str);
+}
+
+static void request_name_callback(struct l_dbus *dbus, bool success,
+					bool queued, void *user_data)
+{
+	l_info("Request name %s",
+		success ? "success": "failed");
+
+	if (success)
+		dbus_init(dbus);
+	else
+		l_main_quit();
+}
+
+static void ready_callback(void *user_data)
+{
+	struct l_dbus *dbus = user_data;
+
+	l_info("D-Bus ready");
+	l_dbus_name_acquire(dbus, BLUEZ_MESH_NAME, false, false, false,
+						request_name_callback, NULL);
+
+}
+
+static void disconnect_callback(void *user_data)
+{
+	l_main_quit();
+}
+
 static void signal_handler(uint32_t signo, void *user_data)
 {
 	static bool terminated;
@@ -78,8 +115,9 @@ int main(int argc, char *argv[])
 {
 	int status;
 	bool detached = true;
-	struct bt_mesh *mesh = NULL;
-	const char *config_file = NULL;
+	bool dbus_debug = false;
+	struct l_dbus *dbus = NULL;
+	const char *config_dir = NULL;
 	int index = MGMT_INDEX_NONE;
 
 	if (!l_main_init())
@@ -91,7 +129,7 @@ int main(int argc, char *argv[])
 		int opt;
 		const char *str;
 
-		opt = getopt_long(argc, argv, "i:c:ndh", main_options, NULL);
+		opt = getopt_long(argc, argv, "i:c:ndbh", main_options, NULL);
 		if (opt < 0)
 			break;
 
@@ -117,7 +155,10 @@ int main(int argc, char *argv[])
 			l_debug_enable("*");
 			break;
 		case 'c':
-			config_file = optarg;
+			config_dir = optarg;
+			break;
+		case 'b':
+			dbus_debug = true;
 			break;
 		case 'h':
 			usage();
@@ -130,7 +171,7 @@ int main(int argc, char *argv[])
 		}
 	}
 
-	if (!mesh_new(index, config_file)) {
+	if (!mesh_init(index, config_dir)) {
 		l_error("Failed to initialize mesh");
 		status = EXIT_FAILURE;
 		goto done;
@@ -138,6 +179,24 @@ int main(int argc, char *argv[])
 
 	umask(0077);
 
+	dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
+	if (!dbus) {
+		l_error("unable to connect to D-Bus");
+		status = EXIT_FAILURE;
+		goto done;
+	}
+
+	if (dbus_debug)
+		l_dbus_set_debug(dbus, do_debug, "[DBUS] ", NULL);
+	l_dbus_set_ready_handler(dbus, ready_callback, dbus, NULL);
+	l_dbus_set_disconnect_handler(dbus, disconnect_callback, NULL, NULL);
+
+	if (!l_dbus_object_manager_enable(dbus)) {
+		l_error("Failed to enable Object Manager");
+		status = EXIT_FAILURE;
+		goto done;
+	}
+
 	if (detached) {
 		if (daemon(0, 0)) {
 			perror("Failed to start meshd daemon");
@@ -149,8 +208,8 @@ int main(int argc, char *argv[])
 	status = l_main_run_with_signal(signal_handler, NULL);
 
 done:
-	mesh_unref(mesh);
 	mesh_cleanup();
+	l_dbus_destroy(dbus);
 	l_main_exit();
 
 	return status;
-- 
2.14.5


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

* [PATCH BlueZ v6 23/26] mesh: Add default location for Mesh Node storage
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (21 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 22/26] mesh: Update for DBus API and multi-node support Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 24/26] mesh: Sample Provisioning Agent Brian Gix
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

---
 configure.ac | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/configure.ac b/configure.ac
index 1a095a352..ae64ddc0f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -364,6 +364,9 @@ AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}",
 			[Directory for the configuration files])
 AC_SUBST(CONFIGDIR, "${configdir}")
 
+AC_DEFINE_UNQUOTED(MESH_STORAGEDIR, "${storagedir}/mesh",
+			[Directory for the mesh daemon storage files])
+
 AC_ARG_ENABLE(android, AC_HELP_STRING([--enable-android],
 			[enable BlueZ for Android]),
 					[enable_android=${enableval}])
-- 
2.14.5


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

* [PATCH BlueZ v6 24/26] mesh: Sample Provisioning Agent
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (22 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 23/26] mesh: Add default location for Mesh Node storage Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 25/26] mesh: Sample On/Off Client and Server Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 26/26] mesh: Sample Mesh Joiner (provision acceptor) Brian Gix
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

---
 test/agent.py | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100755 test/agent.py

diff --git a/test/agent.py b/test/agent.py
new file mode 100755
index 000000000..22c92f952
--- /dev/null
+++ b/test/agent.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+AGENT_IFACE = 'org.bluez.mesh.ProvisionAgent1'
+AGENT_PATH = "/mesh/test/agent"
+
+bus = None
+
+class Agent(dbus.service.Object):
+	def __init__(self, bus):
+		self.path = AGENT_PATH
+		self.bus = bus
+		dbus.service.Object.__init__(self, bus, self.path)
+
+	def get_properties(self):
+		caps = []
+		oob = []
+		caps.append('out-numeric')
+		oob.append('other')
+		return {
+			AGENT_IFACE: {
+				'Capabilities': dbus.Array(caps, 's'),
+				'OutOfBandInfo': dbus.Array(oob, 's')
+			}
+		}
+
+	def get_path(self):
+		return dbus.ObjectPath(self.path)
+
+	@dbus.service.method(AGENT_IFACE, in_signature="", out_signature="")
+	def Cancel(self):
+		print("Cancel")
+
+	@dbus.service.method(AGENT_IFACE, in_signature="su", out_signature="")
+	def DisplayNumeric(self, type, value):
+		print("DisplayNumeric type=", type, " number=", value)
-- 
2.14.5


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

* [PATCH BlueZ v6 25/26] mesh: Sample On/Off Client and Server
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (23 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 24/26] mesh: Sample Provisioning Agent Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  2018-12-28 22:07 ` [PATCH BlueZ v6 26/26] mesh: Sample Mesh Joiner (provision acceptor) Brian Gix
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

---
 test/example-onoff-client | 288 ++++++++++++++++++++++++++++++++++++
 test/example-onoff-server | 365 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 653 insertions(+)
 create mode 100644 test/example-onoff-client
 create mode 100644 test/example-onoff-server

diff --git a/test/example-onoff-client b/test/example-onoff-client
new file mode 100644
index 000000000..e4a87eb12
--- /dev/null
+++ b/test/example-onoff-client
@@ -0,0 +1,288 @@
+#!/usr/bin/env python3
+
+import sys
+import struct
+import numpy
+import dbus
+import dbus.service
+import dbus.exceptions
+
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+from dbus.mainloop.glib import DBusGMainLoop
+
+MESH_SERVICE_NAME = 'org.bluez.mesh'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+
+MESH_NETWORK_IFACE = 'org.bluez.mesh.Network1'
+MESH_NODE_IFACE = 'org.bluez.mesh.Node1'
+MESH_ELEMENT_IFACE = 'org.bluez.mesh.Element1'
+
+VENDOR_ID_NONE = 0xffff
+
+app = None
+bus = None
+mainloop = None
+node = None
+token = numpy.uint64(0x76bd4f2372477600)
+
+def unwrap(item):
+	if isinstance(item, dbus.Boolean):
+		return bool(item)
+	if isinstance(item, (dbus.UInt16, dbus.Int16, dbus.UInt32, dbus.Int32,
+						dbus.UInt64, dbus.Int64)):
+		return int(item)
+	if isinstance(item, dbus.Byte):
+		return bytes([int(item)])
+	if isinstance(item, dbus.String):
+			return item
+	if isinstance(item, (dbus.Array, list, tuple)):
+		return [unwrap(x) for x in item]
+	if isinstance(item, (dbus.Dictionary, dict)):
+		return dict([(unwrap(x), unwrap(y)) for x, y in item.items()])
+
+	print('Dictionary item not handled')
+	print(type(item))
+	return item
+
+def attach_app_cb(node_path, dict_array):
+	print('Mesh application registered ', node_path)
+	print(type(node_path))
+	print(type(dict_array))
+	print(dict_array)
+
+	els = unwrap(dict_array)
+	print("Get Elements")
+	for el in els:
+		print(el)
+		idx = struct.unpack('b', el[0])[0]
+		print('Configuration for Element ', end='')
+		print(idx)
+		models = el[1]
+
+		element = app.get_element(idx)
+		element.set_model_config(models)
+
+	obj = bus.get_object(MESH_SERVICE_NAME, node_path)
+	global node
+	node = dbus.Interface(obj, MESH_NODE_IFACE)
+
+def error_cb(error):
+	print('D-Bus call failed: ' + str(error))
+
+def generic_reply_cb():
+	print('D-Bus call done')
+
+def interfaces_removed_cb(object_path, interfaces):
+	if not mesh_net:
+		return
+
+	if object_path == mesh_net[2]:
+		print('Service was removed')
+		mainloop.quit()
+
+class Application(dbus.service.Object):
+
+	def __init__(self, bus):
+		self.path = '/example'
+		self.elements = []
+		dbus.service.Object.__init__(self, bus, self.path)
+
+	def get_path(self):
+		return dbus.ObjectPath(self.path)
+
+	def add_element(self, element):
+		self.elements.append(element)
+
+	def get_element(self, idx):
+		for ele in self.elements:
+			if ele.get_index() == idx:
+				return ele
+
+	@dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+	def GetManagedObjects(self):
+		response = {}
+		print('GetManagedObjects')
+		for element in self.elements:
+			response[element.get_path()] = element.get_properties()
+		return response
+
+class Element(dbus.service.Object):
+	PATH_BASE = '/example/ele'
+
+	def __init__(self, bus, index):
+		self.path = self.PATH_BASE + format(index, '02x')
+		print(self.path)
+		self.models = []
+		self.bus = bus
+		self.index = index
+		dbus.service.Object.__init__(self, bus, self.path)
+
+	def _get_sig_models(self):
+		ids = []
+		for model in self.models:
+			id = model.get_id()
+			vendor = model.get_vendor()
+			if vendor == VENDOR_ID_NONE:
+				ids.append(id)
+		return ids
+
+	def get_properties(self):
+		return {
+				MESH_ELEMENT_IFACE: {
+				'Index': dbus.Byte(self.index),
+				'Models': dbus.Array(
+					self._get_sig_models(), signature='q')
+				}
+		}
+
+	def add_model(self, model):
+		model.set_path(self.path)
+		self.models.append(model)
+
+	def get_index(self):
+		return self.index
+
+	def set_model_config(self, config):
+		print('Set element models config')
+
+	@dbus.service.method(MESH_ELEMENT_IFACE,
+					in_signature="qqbay", out_signature="")
+	def MessageReceived(self, source, key, is_sub, data):
+		print('Message Received on Element ', end='')
+		print(self.index)
+		for model in self.models:
+			model.process_message(source, key, data)
+
+	@dbus.service.method(MESH_ELEMENT_IFACE,
+					in_signature="qa{sv}", out_signature="")
+
+	def UpdateModelConfiguration(self, model_id, config):
+		print('UpdateModelConfig ', end='')
+		print(hex(model_id))
+		for model in self.models:
+			if model_id == model.get_id():
+				model.set_config(config)
+				return
+
+	@dbus.service.method(MESH_ELEMENT_IFACE,
+					in_signature="", out_signature="")
+	def get_path(self):
+		return dbus.ObjectPath(self.path)
+
+class Model():
+	def __init__(self, model_id):
+		self.cmd_ops = []
+		self.model_id = model_id
+		self.vendor = VENDOR_ID_NONE
+		self.path = None
+
+	def set_path(self, path):
+		self.path = path
+
+	def get_id(self):
+		return self.model_id
+
+	def get_vendor(self):
+		return self.vendor
+
+	def process_message(self, source, key, data):
+		print('Model process message')
+
+	def set_publication(self, period):
+		self.period = period
+
+	def set_bindings(self, bindings):
+		self.bindings = bindings
+
+	def set_config(self, config):
+		if 'Bindings' in config:
+			self.bindings = config.get('Bindings')
+			print('Bindings: ', end='')
+			print(self.bindings)
+		if 'PublicationPeriod' in config:
+			self.set_publication(config.get('PublicationPeriod'))
+			print('Model publication period ', end='')
+			print(self.pub_period, end='')
+			print(' ms')
+
+class OnOffClient(Model):
+	def __init__(self, model_id):
+		Model.__init__(self, model_id)
+		self.cmd_ops = { 0x8201, # get
+						 0x8202, # set
+						 0x8203 } # set unacknowledged
+		print('OnOff Client')
+
+	def _reply_cb(state):
+		print('State ', end='');
+		print(state)
+
+	def _send_message(self, dest, key, data, reply_cb):
+		print('OnOffClient send data')
+		node.Send(self.path, dest, key, data, reply_handler=reply_cb,
+				  error_handler=error_cb)
+
+	def get_state(self, dest, key):
+		opcode = 0x8201
+		data = struct.pack('<H', opcode)
+		self._send_message(dest, key, data, self._reply_cb)
+
+	def set_state(self, dest, key, state):
+		opcode = 0x8202
+		data = struct.pack('<HB', opcode, state)
+		self._send_message(dest, key, data, self._reply_cb)
+
+	def process_message(self, source, key, data):
+		print('OnOffClient process message len ', end = '')
+		datalen = len(data)
+		print(datalen)
+
+		if datalen!=3:
+			return
+
+		opcode, state=struct.unpack('<HB',bytes(data))
+		if opcode != 0x8202 :
+			print('Bad opcode ', end='')
+			print(hex(opcode))
+			return
+
+		print('Got state ', end = '')
+		print(hex(state))
+
+def attach_app_error_cb(error):
+	print('Failed to register application: ' + str(error))
+	mainloop.quit()
+
+def main():
+
+	DBusGMainLoop(set_as_default=True)
+
+	global bus
+	bus = dbus.SystemBus()
+	global mainloop
+	global app
+
+	mesh_net = dbus.Interface(bus.get_object(MESH_SERVICE_NAME,
+							"/org/bluez/mesh"),
+							MESH_NETWORK_IFACE)
+	mesh_net.connect_to_signal('InterfacesRemoved', interfaces_removed_cb)
+
+	app = Application(bus)
+	first_ele = Element(bus, 0x00)
+	first_ele.add_model(OnOffClient(0x1001))
+	app.add_element(first_ele)
+
+	mainloop = GObject.MainLoop()
+
+	print('Attach')
+	mesh_net.Attach(app.get_path(), token,
+					reply_handler=attach_app_cb,
+					error_handler=attach_app_error_cb)
+	mainloop.run()
+
+if __name__ == '__main__':
+	main()
diff --git a/test/example-onoff-server b/test/example-onoff-server
new file mode 100644
index 000000000..131b6415c
--- /dev/null
+++ b/test/example-onoff-server
@@ -0,0 +1,365 @@
+#!/usr/bin/env python3
+
+import sys
+import struct
+import numpy
+import dbus
+import dbus.service
+import dbus.exceptions
+
+from threading import Timer
+import time
+
+
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+from dbus.mainloop.glib import DBusGMainLoop
+
+MESH_SERVICE_NAME = 'org.bluez.mesh'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+
+MESH_NETWORK_IFACE = 'org.bluez.mesh.Network1'
+MESH_NODE_IFACE = 'org.bluez.mesh.Node1'
+MESH_APPLICATION_IFACE = 'org.bluez.mesh.Application1'
+MESH_ELEMENT_IFACE = 'org.bluez.mesh.Element1'
+
+APP_COMPANY_ID = 0x05f1
+APP_PRODUCT_ID = 0x0001
+APP_VERSION_ID = 0x0001
+
+VENDOR_ID_NONE = 0xffff
+
+app = None
+bus = None
+mainloop = None
+node = None
+
+token = numpy.uint64(0x76bd4f2372476578)
+
+def generic_error_cb(error):
+	print('D-Bus call failed: ' + str(error))
+
+def generic_reply_cb():
+	print('D-Bus call done')
+
+def unwrap(item):
+	if isinstance(item, dbus.Boolean):
+		return bool(item)
+	if isinstance(item, (dbus.UInt16, dbus.Int16, dbus.UInt32, dbus.Int32,
+						dbus.UInt64, dbus.Int64)):
+		return int(item)
+	if isinstance(item, dbus.Byte):
+		return bytes([int(item)])
+	if isinstance(item, dbus.String):
+			return item
+	if isinstance(item, (dbus.Array, list, tuple)):
+		return [unwrap(x) for x in item]
+	if isinstance(item, (dbus.Dictionary, dict)):
+		return dict([(unwrap(x), unwrap(y)) for x, y in item.items()])
+
+	print('Dictionary item not handled')
+	print(type(item))
+	return item
+
+def attach_app_cb(node_path, dict_array):
+	print('Mesh application registered ', node_path)
+
+	obj = bus.get_object(MESH_SERVICE_NAME, node_path)
+
+	global node
+	node = dbus.Interface(obj, MESH_NODE_IFACE)
+
+	els = unwrap(dict_array)
+	print("Get Elements")
+
+	for el in els:
+		idx = struct.unpack('b', el[0])[0]
+		print('Configuration for Element ', end='')
+		print(idx)
+
+		models = el[1]
+		element = app.get_element(idx)
+		element.set_model_config(models)
+
+def interfaces_removed_cb(object_path, interfaces):
+	if not mesh_net:
+		return
+
+	if object_path == mesh_net[2]:
+		print('Service was removed')
+		mainloop.quit()
+
+def send_response(path, dest, key, data):
+		print('send response ', end='')
+		print(data)
+		node.Send(path, dest, key, data, reply_handler=generic_reply_cb,
+						error_handler=generic_error_cb)
+
+def send_publication(path, model_id, data):
+		print('send publication ', end='')
+		print(data)
+		node.Publish(path, model_id, data,
+						reply_handler=generic_reply_cb,
+						error_handler=generic_error_cb)
+
+class PubTimer():
+	def __init__(self):
+		self.seconds = None
+		self.func = None
+		self.thread = None
+		self.busy = False
+
+	def _timeout_cb(self):
+		self.func()
+		self.busy = True
+		self._schedule_timer()
+		self.busy =False
+
+	def _schedule_timer(self):
+		self.thread = Timer(self.seconds, self._timeout_cb)
+		self.thread.start()
+
+	def start(self, seconds, func):
+		self.func = func
+		self.seconds = seconds
+		if not self.busy:
+			self._schedule_timer()
+
+	def cancel(self):
+		print('Cancel timer')
+		if self.thread is not None:
+			print('Cancel thread')
+			self.thread.cancel()
+			self.thread = None
+
+class Application(dbus.service.Object):
+
+	def __init__(self, bus):
+		self.path = '/example'
+		self.elements = []
+		dbus.service.Object.__init__(self, bus, self.path)
+
+	def get_path(self):
+		return dbus.ObjectPath(self.path)
+
+	def add_element(self, element):
+		self.elements.append(element)
+
+	def get_element(self, idx):
+		for ele in self.elements:
+			if ele.get_index() == idx:
+				return ele
+
+	def get_properties(self):
+		return {
+			MESH_APPLICATION_IFACE: {
+				'CompanyID': dbus.UInt16(APP_COMPANY_ID),
+				'ProductID': dbus.UInt16(APP_PRODUCT_ID),
+				'VersionID': dbus.UInt16(APP_VERSION_ID)
+			}
+		}
+
+	@dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+	def GetManagedObjects(self):
+		response = {}
+		print('GetManagedObjects')
+		response[self.path] = self.get_properties()
+		for element in self.elements:
+			response[element.get_path()] = element.get_properties()
+		return response
+
+class Element(dbus.service.Object):
+	PATH_BASE = '/example/ele'
+
+	def __init__(self, bus, index):
+		self.path = self.PATH_BASE + format(index, '02x')
+		print(self.path)
+		self.models = []
+		self.bus = bus
+		self.index = index
+		dbus.service.Object.__init__(self, bus, self.path)
+
+	def _get_sig_models(self):
+		ids = []
+		for model in self.models:
+			id = model.get_id()
+			vendor = model.get_vendor()
+			if vendor == VENDOR_ID_NONE:
+				ids.append(id)
+		return ids
+
+	def get_properties(self):
+		return {
+			MESH_ELEMENT_IFACE: {
+				'Index': dbus.Byte(self.index),
+				'Models': dbus.Array(
+					self._get_sig_models(), signature='q')
+			}
+		}
+
+	def add_model(self, model):
+		model.set_path(self.path)
+		self.models.append(model)
+
+	def get_index(self):
+		return self.index
+
+	def set_model_config(self, configs):
+		print('Set element models config')
+		for config in configs:
+			mod_id = config[0]
+			self.UpdateModelConfiguration(mod_id, config[1])
+
+	@dbus.service.method(MESH_ELEMENT_IFACE,
+					in_signature="qqbay", out_signature="")
+	def MessageReceived(self, source, key, is_sub, data):
+		print('Message Received on Element ', end='')
+		print(self.index)
+		for model in self.models:
+			model.process_message(source, key, data)
+
+	@dbus.service.method(MESH_ELEMENT_IFACE,
+					in_signature="qa{sv}", out_signature="")
+
+	def UpdateModelConfiguration(self, model_id, config):
+		print('UpdateModelConfig ', end='')
+		print(hex(model_id))
+		for model in self.models:
+			if model_id == model.get_id():
+				model.set_config(config)
+				return
+
+	@dbus.service.method(MESH_ELEMENT_IFACE,
+					in_signature="", out_signature="")
+
+	def get_path(self):
+		return dbus.ObjectPath(self.path)
+
+class Model():
+	def __init__(self, model_id):
+		self.cmd_ops = []
+		self.model_id = model_id
+		self.vendor = VENDOR_ID_NONE
+		self.bindings = []
+		self.pub_period = 0
+		self.pub_id = 0
+		self.path = None
+
+	def set_path(self, path):
+		self.path = path
+
+	def get_id(self):
+		return self.model_id
+
+	def get_vendor(self):
+		return self.vendor
+
+	def process_message(self, source, key, data):
+		print('Model process message')
+
+	def set_publication(self, period):
+		self.pub_period = period
+
+	def set_config(self, config):
+		if 'Bindings' in config:
+			self.bindings = config.get('Bindings')
+			print('Bindings: ', end='')
+			print(self.bindings)
+		if 'PublicationPeriod' in config:
+			self.set_publication(config.get('PublicationPeriod'))
+			print('Model publication period ', end='')
+			print(self.pub_period, end='')
+			print(' ms')
+
+class OnOffServer(Model):
+	def __init__(self, model_id):
+		Model.__init__(self, model_id)
+		self.cmd_ops = { 0x8201, # get
+						 0x8202, # set
+						 0x8203 } # set unacknowledged
+
+		print("OnOff Server ", end="")
+		self.state = 0
+		print('State ', end='')
+		self.timer = PubTimer()
+
+	def process_message(self, source, key, data):
+		datalen = len(data)
+		print('OnOff Server process message len ', datalen)
+
+		if datalen!=2 and datalen!=3:
+			return
+
+		if datalen==2:
+			op_tuple=struct.unpack('<H',bytes(data))
+			opcode = op_tuple[0]
+			if opcode != 0x8201:
+				print(hex(opcode))
+				return
+			print('Get state')
+		elif datalen==3:
+			opcode,self.state=struct.unpack('<HB',bytes(data))
+			if opcode != 0x8202 and opcode != 0x8203:
+				print(hex(opcode))
+				return
+			print('Set state: ', end='')
+			print(self.state)
+
+		rsp_data = struct.pack('<HB', 0x8204, self.state)
+		send_response(self.path, source, key, rsp_data)
+
+	def publish(self):
+		print('Publish')
+		data = struct.pack('B', self.state)
+		send_publication(self.path, self.model_id, data)
+
+	def set_publication(self, period):
+		if period == 0:
+			self.pub_period = 0
+			self.timer.cancel()
+			return
+
+		# We do not handle ms in this example
+		if period < 1000:
+			return
+
+		self.pub_period = period
+		self.timer.start(period/1000, self.publish)
+
+def attach_app_error_cb(error):
+	print('Failed to register application: ' + str(error))
+	mainloop.quit()
+
+def main():
+
+	DBusGMainLoop(set_as_default=True)
+
+	global bus
+	bus = dbus.SystemBus()
+	global mainloop
+	global app
+
+	mesh_net = dbus.Interface(bus.get_object(MESH_SERVICE_NAME,
+						"/org/bluez/mesh"),
+						MESH_NETWORK_IFACE)
+	mesh_net.connect_to_signal('InterfacesRemoved', interfaces_removed_cb)
+
+	app = Application(bus)
+	first_ele = Element(bus, 0x00)
+	first_ele.add_model(OnOffServer(0x1000))
+	app.add_element(first_ele)
+
+	mainloop = GObject.MainLoop()
+
+	print('Attach')
+	mesh_net.Attach(app.get_path(), token,
+					reply_handler=attach_app_cb,
+					error_handler=attach_app_error_cb)
+
+	mainloop.run()
+
+if __name__ == '__main__':
+	main()
-- 
2.14.5


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

* [PATCH BlueZ v6 26/26] mesh: Sample Mesh Joiner (provision acceptor)
  2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
                   ` (24 preceding siblings ...)
  2018-12-28 22:07 ` [PATCH BlueZ v6 25/26] mesh: Sample On/Off Client and Server Brian Gix
@ 2018-12-28 22:07 ` Brian Gix
  25 siblings, 0 replies; 27+ messages in thread
From: Brian Gix @ 2018-12-28 22:07 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: johan.hedberg, inga.stotland, marcel, brian.gix

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

---
 test/test-join | 408 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 408 insertions(+)
 create mode 100644 test/test-join

diff --git a/test/test-join b/test/test-join
new file mode 100644
index 000000000..cdf92a2f1
--- /dev/null
+++ b/test/test-join
@@ -0,0 +1,408 @@
+#!/usr/bin/env python3
+
+import sys
+import struct
+import numpy
+import dbus
+import dbus.service
+import dbus.exceptions
+
+from threading import Timer
+import time
+
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+from dbus.mainloop.glib import DBusGMainLoop
+
+import agent
+
+MESH_SERVICE_NAME = 'org.bluez.mesh'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+
+MESH_NETWORK_IFACE = 'org.bluez.mesh.Network1'
+MESH_NODE_IFACE = 'org.bluez.mesh.Node1'
+MESH_APPLICATION_IFACE = 'org.bluez.mesh.Application1'
+MESH_ELEMENT_IFACE = 'org.bluez.mesh.Element1'
+
+APP_COMPANY_ID = 0x05f1
+APP_PRODUCT_ID = 0x0001
+APP_VERSION_ID = 0x0001
+
+VENDOR_ID_NONE = 0xffff
+
+mesh_net = None
+app = None
+bus = None
+mainloop = None
+node = None
+
+token = None
+
+def generic_error_cb(error):
+	print('D-Bus call failed: ' + str(error))
+
+def generic_reply_cb():
+	print('D-Bus call done')
+
+def unwrap(item):
+	if isinstance(item, dbus.Boolean):
+		return bool(item)
+	if isinstance(item, (dbus.UInt16, dbus.Int16,
+			 dbus.UInt32, dbus.Int32,
+			 dbus.UInt64, dbus.Int64)):
+		return int(item)
+	if isinstance(item, dbus.Byte):
+		return bytes([int(item)])
+	if isinstance(item, dbus.String):
+		return item
+	if isinstance(item, (dbus.Array, list, tuple)):
+		return [unwrap(x) for x in item]
+	if isinstance(item, (dbus.Dictionary, dict)):
+		return dict([(unwrap(x), unwrap(y)) for x, y in item.items()])
+
+	print('Dictionary item not handled')
+	print(type(item))
+	return item
+
+def join_cb():
+	print('Join procedure started')
+
+def join_error_cb(reason):
+	print('Join procedure failed: ', reason)
+
+def attach_app_cb(node_path, dict_array):
+	print('Mesh application registered ', node_path)
+
+	obj = bus.get_object(MESH_SERVICE_NAME, node_path)
+
+	global node
+	node = dbus.Interface(obj, MESH_NODE_IFACE)
+
+	els = unwrap(dict_array)
+	print("Get Elements")
+
+	for el in els:
+		idx = struct.unpack('b', el[0])[0]
+		print('Configuration for Element ', end='')
+		print(idx)
+
+		models = el[1]
+		element = app.get_element(idx)
+		element.set_model_config(models)
+
+def attach_app_error_cb(error):
+	print('Failed to register application: ' + str(error))
+	mainloop.quit()
+
+def attach(token):
+	print('Attach')
+	mesh_net.Attach(app.get_path(), token,
+					reply_handler=attach_app_cb,
+					error_handler=attach_app_error_cb)
+
+def interfaces_removed_cb(object_path, interfaces):
+	if not mesh_net:
+		return
+
+	if object_path == mesh_net[2]:
+		print('Service was removed')
+	mainloop.quit()
+
+def send_response(path, dest, key, data):
+	print('send response ', end='')
+	print(data)
+	node.Send(path, dest, key, data, reply_handler=generic_reply_cb,
+					error_handler=generic_error_cb)
+
+def send_publication(path, model_id, data):
+	print('send publication ', end='')
+	print(data)
+	node.Publish(path, model_id, data, reply_handler=generic_reply_cb,
+			error_handler=generic_error_cb)
+
+class PubTimer():
+	def __init__(self):
+		self.seconds = None
+		self.func = None
+		self.thread = None
+		self.busy = False
+
+	def _timeout_cb(self):
+		self.func()
+		self.busy = True
+		self._schedule_timer()
+		self.busy =False
+
+	def _schedule_timer(self):
+		self.thread = Timer(self.seconds, self._timeout_cb)
+		self.thread.start()
+
+	def start(self, seconds, func):
+		self.func = func
+		self.seconds = seconds
+		if not self.busy:
+			self._schedule_timer()
+
+	def cancel(self):
+		print('Cancel timer')
+		if self.thread is not None:
+			print('Cancel thread')
+			self.thread.cancel()
+			self.thread = None
+
+class Application(dbus.service.Object):
+
+	def __init__(self, bus):
+		self.path = '/example'
+		self.agent = None
+		self.elements = []
+		dbus.service.Object.__init__(self, bus, self.path)
+
+	def set_agent(self, agent):
+		self.agent = agent
+
+	def get_path(self):
+		return dbus.ObjectPath(self.path)
+
+	def add_element(self, element):
+		self.elements.append(element)
+
+	def get_element(self, idx):
+		for ele in self.elements:
+			if ele.get_index() == idx:
+				return ele
+
+	def get_properties(self):
+		return {
+			MESH_APPLICATION_IFACE: {
+			'CompanyID': dbus.UInt16(APP_COMPANY_ID),
+			'ProductID': dbus.UInt16(APP_PRODUCT_ID),
+			'VersionID': dbus.UInt16(APP_VERSION_ID)
+			}
+		}
+
+	@dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+
+	def GetManagedObjects(self):
+		response = {}
+		print('GetManagedObjects')
+		response[self.path] = self.get_properties()
+		response[self.agent.get_path()] = self.agent.get_properties()
+		for element in self.elements:
+			response[element.get_path()] = element.get_properties()
+		return response
+
+	@dbus.service.method(MESH_APPLICATION_IFACE,
+					in_signature="t", out_signature="")
+
+	def JoinComplete(self, value):
+		global token
+		print('JoinComplete ', value)
+
+		token = value
+		attach(token)
+
+	@dbus.service.method(MESH_APPLICATION_IFACE,
+					in_signature="s", out_signature="")
+
+	def JoinFailed(self, value):
+		print('JoinFailed ', value)
+		token = value
+
+class Element(dbus.service.Object):
+	PATH_BASE = '/example/ele'
+
+	def __init__(self, bus, index):
+		self.path = self.PATH_BASE + format(index, '02x')
+		print(self.path)
+		self.models = []
+		self.bus = bus
+		self.index = index
+		dbus.service.Object.__init__(self, bus, self.path)
+
+	def _get_sig_models(self):
+		ids = []
+		for model in self.models:
+			id = model.get_id()
+			vendor = model.get_vendor()
+			if vendor == VENDOR_ID_NONE:
+				ids.append(id)
+		return ids
+
+	def get_properties(self):
+		return {
+			MESH_ELEMENT_IFACE: {
+			'Index': dbus.Byte(self.index),
+			'Models': dbus.Array(self._get_sig_models(), 'q')
+			}
+		}
+
+	def add_model(self, model):
+		model.set_path(self.path)
+		self.models.append(model)
+
+	def get_index(self):
+		return self.index
+
+	def set_model_config(self, configs):
+		print('Set element models config')
+		for config in configs:
+			mod_id = config[0]
+			self.UpdateModelConfiguration(mod_id, config[1])
+
+	@dbus.service.method(MESH_ELEMENT_IFACE,
+					in_signature="qqbay", out_signature="")
+	def MessageReceived(self, source, key, is_sub, data):
+		print('Message Received on Element ', end='')
+		print(self.index)
+		for model in self.models:
+			model.process_message(source, key, data)
+
+	@dbus.service.method(MESH_ELEMENT_IFACE,
+					in_signature="qa{sv}", out_signature="")
+
+	def UpdateModelConfiguration(self, model_id, config):
+		print('UpdateModelConfig ', end='')
+		print(hex(model_id))
+		for model in self.models:
+			if model_id == model.get_id():
+				model.set_config(config)
+				return
+
+	@dbus.service.method(MESH_ELEMENT_IFACE,
+					in_signature="", out_signature="")
+
+	def get_path(self):
+		return dbus.ObjectPath(self.path)
+
+class Model():
+	def __init__(self, model_id):
+		self.cmd_ops = []
+		self.model_id = model_id
+		self.vendor = VENDOR_ID_NONE
+		self.bindings = []
+		self.pub_period = 0
+		self.pub_id = 0
+		self.path = None
+
+	def set_path(self, path):
+		self.path = path
+
+	def get_id(self):
+		return self.model_id
+
+	def get_vendor(self):
+		return self.vendor
+
+	def process_message(self, source, key, data):
+		print('Model process message')
+
+	def set_publication(self, period):
+		self.pub_period = period
+
+	def set_config(self, config):
+		if 'Bindings' in config:
+			self.bindings = config.get('Bindings')
+			print('Bindings: ', end='')
+			print(self.bindings)
+		if 'PublicationPeriod' in config:
+			self.set_publication(config.get('PublicationPeriod'))
+			print('Model publication period ', end='')
+			print(self.pub_period, end='')
+			print(' ms')
+
+class OnOffServer(Model):
+	def __init__(self, model_id):
+		Model.__init__(self, model_id)
+		self.cmd_ops = { 0x8201, # get
+				 0x8202, # set
+				 0x8203 } # set unacknowledged
+
+		print("OnOff Server ", end="")
+		self.state = 0
+		print('State ', end='')
+		self.timer = PubTimer()
+
+	def process_message(self, source, key, data):
+		datalen = len(data)
+		print('OnOff Server process message len ', datalen)
+
+		if datalen!=2 and datalen!=3:
+			return
+
+		if datalen==2:
+			op_tuple=struct.unpack('<H',bytes(data))
+			opcode = op_tuple[0]
+			if opcode != 0x8201:
+				print(hex(opcode))
+				return
+			print('Get state')
+		elif datalen==3:
+			opcode,self.state=struct.unpack('<HB',bytes(data))
+			if opcode != 0x8202 and opcode != 0x8203:
+				print(hex(opcode))
+				return
+			print('Set state: ', end='')
+			print(self.state)
+
+		rsp_data = struct.pack('<HB', 0x8204, self.state)
+		send_response(self.path, source, key, rsp_data)
+
+	def publish(self):
+		print('Publish')
+		data = struct.pack('B', self.state)
+		send_publication(self.path, self.model_id, data)
+
+	def set_publication(self, period):
+		if period == 0:
+			self.pub_period = 0
+			self.timer.cancel()
+			return
+
+		# We do not handle ms in this example
+		if period < 1000:
+			return
+
+		self.pub_period = period
+		self.timer.start(period/1000, self.publish)
+
+def main():
+
+	DBusGMainLoop(set_as_default=True)
+
+	global bus
+	bus = dbus.SystemBus()
+	global mainloop
+	global app
+	global mesh_net
+
+	mesh_net = dbus.Interface(bus.get_object(MESH_SERVICE_NAME,
+						 "/org/bluez/mesh"),
+						 MESH_NETWORK_IFACE)
+	mesh_net.connect_to_signal('InterfacesRemoved', interfaces_removed_cb)
+
+	app = Application(bus)
+	prov_agent = agent.Agent(bus)
+	app.set_agent(prov_agent)
+	first_ele = Element(bus, 0x00)
+	first_ele.add_model(OnOffServer(0x1000))
+	app.add_element(first_ele)
+
+	mainloop = GObject.MainLoop()
+
+	print('Join')
+	caps = ["out-numeric"]
+	oob = ["other"]
+	uuid = bytearray.fromhex("0a0102030405060708090A0B0C0D0E0F")
+	print(uuid)
+	mesh_net.Join(app.get_path(), uuid,
+			reply_handler=join_cb,
+			error_handler=join_error_cb)
+
+	mainloop.run()
+
+if __name__ == '__main__':
+	main()
-- 
2.14.5


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

end of thread, back to index

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-28 22:07 [PATCH BlueZ v6 00/26] Major rewrite for Multi-Node and DBus Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 01/26] mesh: Structural changes for mesh Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 02/26] mesh: Utilities for DBus support Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 03/26] mesh: Internal errors Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 04/26] mesh: Rewrite storage for Multiple Nodes Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 05/26] mesh: Rewrite Node handling for multiple nodes Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 06/26] mesh: Rewrite Network layer " Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 07/26] mesh: Direction agnostic PB-ADV implementation Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 08/26] mesh: Acceptor side provisioning implementation Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 09/26] mesh: Initiator " Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 10/26] mesh: Rewrite Controler interface for full init Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 11/26] mesh: Unchanged variables set to const Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 12/26] mesh: Hex-String manipulation, and debug logging Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 13/26] mesh: re-arrange provisioning for DBus API Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 14/26] mesh: Re-architect " Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 15/26] mesh: Multi node Config Server model Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 16/26] mesh: restructure I/O for multiple nodes Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 17/26] mesh: Restructure DB to support " Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 18/26] mesh: Restructure model services for " Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 19/26] mesh: DBUS interface for Provisioning Agent Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 20/26] mesh: restructure App Key storage Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 21/26] mesh: Clean-up Comment style Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 22/26] mesh: Update for DBus API and multi-node support Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 23/26] mesh: Add default location for Mesh Node storage Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 24/26] mesh: Sample Provisioning Agent Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 25/26] mesh: Sample On/Off Client and Server Brian Gix
2018-12-28 22:07 ` [PATCH BlueZ v6 26/26] mesh: Sample Mesh Joiner (provision acceptor) Brian Gix

Linux-Bluetooth Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-bluetooth/0 linux-bluetooth/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-bluetooth linux-bluetooth/ https://lore.kernel.org/linux-bluetooth \
		linux-bluetooth@vger.kernel.org linux-bluetooth@archiver.kernel.org
	public-inbox-index linux-bluetooth


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-bluetooth


AGPL code for this site: git clone https://public-inbox.org/ public-inbox