[PATCH BlueZ v6 18/26] mesh: Restructure model services for multiple nodes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Inga Stotland <inga.stotland@xxxxxxxxx>

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




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux