From: Brian Gix <brian.gix@intel.com>
To: linux-bluetooth@vger.kernel.org
Cc: Brian Gix <brian.gix@intel.com>
Subject: [PATCH BlueZ v3 06/14] meshd: Upper and Lower mesh transport
Date: Wed, 25 Apr 2018 11:20:34 -0700 [thread overview]
Message-ID: <20180425182042.31938-7-brian.gix@intel.com> (raw)
In-Reply-To: <20180425182042.31938-1-brian.gix@intel.com>
---
meshd/src/net.c | 3639 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 3639 insertions(+)
create mode 100644 meshd/src/net.c
diff --git a/meshd/src/net.c b/meshd/src/net.c
new file mode 100644
index 000000000..12f11b2b2
--- /dev/null
+++ b/meshd/src/net.c
@@ -0,0 +1,3639 @@
+/*
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <ell/ell.h>
+
+#include "meshd/common/mesh-defs.h"
+#include "meshd/common/util.h"
+
+#include "meshd/src/display.h"
+#include "meshd/src/crypto.h"
+#include "meshd/src/mesh.h"
+#include "meshd/src/node.h"
+#include "meshd/src/net.h"
+#include "meshd/src/mesh-io.h"
+#include "meshd/src/friend.h"
+#include "meshd/src/storage.h"
+#include "meshd/src/model.h"
+#include "meshd/src/appkey.h"
+#include "meshd/src/prov.h"
+#include "meshd/src/provision.h"
+
+#define abs_diff(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))
+
+#define IV_IDX_DIFF_RANGE 42
+
+/* #define IV_IDX_UPD_MIN (60) 1 minute for Testing */
+#define IV_IDX_UPD_MIN (60 * 60 * 96) /* 96 Hours - per Spec */
+#define IV_IDX_UPD_HOLD (IV_IDX_UPD_MIN/2)
+#define IV_IDX_UPD_MAX (IV_IDX_UPD_MIN + IV_IDX_UPD_HOLD)
+
+#define iv_is_updating(net) ((net)->iv_upd_state == IV_UPD_UPDATING)
+
+#define IV_UPDATE_SEQ_TRIGGER 0x800000 /* Half of Seq-Nums expended */
+
+#define SEG_TO 2
+#define MSG_TO 60
+
+#define DEFAULT_MIN_DELAY 0
+#define DEFAULT_MAX_DELAY 25
+
+#define DEFAULT_TRANSMIT_COUNT 1
+#define DEFAULT_TRANSMIT_INTERVAL 100
+
+#define BEACON_TYPE_SNB 0x01
+
+#define BEACON_INTERVAL_MIN 10
+#define BEACON_INTERVAL_MAX 600
+
+#define SAR_KEY(src, seq0) ((((uint32_t)(seq0)) << 16) | (src))
+
+enum _iv_upd_state {
+ /* Allows acceptance of any iv_index secure net beacon */
+ IV_UPD_INIT,
+ /* Normal, can transition, accept current or old */
+ IV_UPD_NORMAL,
+ /* Updating proc running, we use old, accept old or new */
+ IV_UPD_UPDATING,
+ /* Normal, can *not* transition, accept current or old iv_index */
+ IV_UPD_NORMAL_HOLD,
+};
+
+struct net_key {
+ struct mesh_key_set key_set;
+ unsigned int beacon_id;
+ uint8_t key[16];
+ uint8_t beacon_key[16];
+ uint8_t network_id[8];
+};
+
+struct mesh_beacon {
+ struct l_timeout *timeout;
+ uint32_t ts;
+ uint16_t observe_period;
+ uint16_t observed;
+ uint16_t expected;
+ uint8_t half_period;
+ uint8_t beacon[23];
+};
+
+struct mesh_subnet {
+ struct mesh_net *net;
+ uint16_t idx;
+ struct net_key *tx;
+ struct net_key current;
+ struct net_key updated;
+ struct mesh_beacon snb;
+ uint8_t key_refresh;
+ uint8_t kr_phase;
+};
+
+struct mesh_net {
+ int ref_count;
+ struct mesh_io *io;
+ struct mesh_node *local_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;
+ unsigned int beacon_id;
+ unsigned int key_id_next;
+ unsigned int sar_id_next;
+
+ bool friend_enable;
+ 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;
+
+ bool iv_update;
+ uint32_t instant; /* Controller Instant of recent Rx */
+ 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;
+ uint16_t tx_interval;
+ uint16_t tx_cnt;
+ uint8_t chan; /* Channel of recent Rx */
+ uint8_t default_ttl;
+ uint8_t tid;
+ uint8_t window_accuracy;
+
+ struct {
+ bool enable;
+ uint16_t interval;
+ uint8_t count;
+ } relay;
+
+ struct mesh_net_heartbeat heartbeat;
+
+ struct l_queue *subnets;
+ struct l_queue *msg_cache;
+ struct l_queue *sar_in;
+ struct l_queue *sar_out;
+ struct l_queue *frnd_msgs;
+ struct l_queue *friends;
+ struct l_queue *destinations;
+ struct l_queue *fast_cache;
+ struct l_queue *key_sets;
+
+ uint8_t prov_priv_key[32];
+
+ /* Unprovisioned Identity */
+ char id_name[20];
+ uint8_t id_uuid[16];
+
+ /* Provisioner: unicast address range */
+ struct mesh_net_addr_range prov_uni_addr;
+
+ /* Test Data */
+ uint8_t prov_rand[16];
+ uint8_t test_bd_addr[6];
+ struct mesh_net_prov_caps prov_caps;
+ bool test_mode;
+};
+
+struct mesh_msg {
+ uint16_t src;
+ uint32_t seq;
+ uint32_t mic;
+};
+
+struct mesh_sar {
+ unsigned int id;
+ struct l_timeout *seg_timeout;
+ struct l_timeout *msg_timeout;
+ mesh_net_status_func_t status_func;
+ void *user_data;
+ uint32_t flags;
+ uint32_t last_nak;
+ uint32_t iv_index;
+ uint32_t seqAuth;
+ uint16_t seqZero;
+ uint16_t app_idx;
+ uint16_t src;
+ uint16_t remote;
+ uint16_t len;
+ bool szmic;
+ bool frnd;
+ bool frnd_cred;
+ uint8_t ttl;
+ uint8_t last_seg;
+ uint8_t key_id;
+ uint8_t buf[4]; /* Large enough for ACK-Flags and MIC */
+};
+
+struct mesh_destination {
+ uint16_t dst;
+ uint16_t ref_cnt;
+};
+
+struct msg_rx {
+ const uint8_t *data;
+ uint32_t iv_index;
+ uint32_t seq;
+ uint16_t src;
+ uint16_t dst;
+ uint16_t size;
+ uint8_t tc;
+ bool done;
+ bool szmic;
+ union {
+ struct {
+ uint16_t app_idx;
+ uint8_t key_id;
+ } m;
+ struct {
+ uint16_t seq0;
+ } a;
+ struct {
+ uint8_t opcode;
+ } c;
+ } u;
+};
+
+struct net_decode {
+ struct mesh_net *net;
+ struct mesh_friend *frnd;
+ struct mesh_key_set *key_set;
+ uint8_t *packet;
+ uint32_t iv_index;
+ uint8_t size;
+ uint8_t nid;
+ bool proxy;
+};
+
+static inline struct mesh_subnet *get_primary_subnet(struct mesh_net *net)
+{
+ return l_queue_peek_head(net->subnets);
+}
+
+static bool match_key_index(const void *a, const void *b)
+{
+ const struct mesh_subnet *subnet = a;
+ uint16_t idx = L_PTR_TO_UINT(b);
+
+ return subnet->idx == idx;
+}
+
+static bool match_key_set(const void *a, const void *b)
+{
+ const struct mesh_subnet *subnet = a;
+ const struct mesh_key_set *key_set = b;
+
+ return (key_set == &subnet->current.key_set) ||
+ (key_set == &subnet->updated.key_set);
+}
+
+static bool match_network_id(const void *a, const void *b)
+{
+ const struct mesh_subnet *subnet = a;
+ const uint8_t *network_id = b;
+
+ return ((memcmp(subnet->current.network_id, network_id, 8) == 0) ||
+ (memcmp(subnet->updated.network_id, network_id, 8) == 0));
+}
+
+static void idle_mesh_heartbeat_send(void *net)
+{
+ mesh_net_heartbeat_send(net);
+}
+
+static void trigger_heartbeat(struct mesh_net *net, uint16_t feature,
+ bool in_use)
+{
+ struct mesh_net_heartbeat *hb = &net->heartbeat;
+
+ l_info("%s: %4.4x --> %d", __func__, feature, in_use);
+ if (in_use) {
+ if (net->heartbeat.features & feature)
+ return; /* no change */
+
+ hb->features |= feature;
+ } else {
+ if (!(hb->features & feature))
+ return; /* no change */
+
+ hb->features &= ~feature;
+ }
+
+ if (!(hb->pub_features & feature))
+ return; /* not interested in this feature */
+
+ l_idle_oneshot(idle_mesh_heartbeat_send, net, NULL);
+
+}
+
+static bool match_by_friend(const void *a, const void *b)
+{
+ const struct mesh_friend *frnd = a;
+ uint16_t dst = L_PTR_TO_UINT(b);
+
+ return frnd->dst == dst;
+}
+
+static void free_friend_internals(struct mesh_friend *frnd)
+{
+ if (frnd->pkt_cache)
+ l_queue_destroy(frnd->pkt_cache, l_free);
+
+ if (frnd->grp_list)
+ l_free(frnd->grp_list);
+
+ frnd->pkt_cache = NULL;
+ frnd->grp_list = NULL;
+ mesh_net_remove_keyset(frnd->net, &frnd->key_set);
+ mesh_net_remove_keyset(frnd->net, &frnd->new_key_set);
+}
+
+static void frnd_kr_phase1(void *a, void *b)
+{
+ struct mesh_friend *frnd = a;
+ const uint8_t *key = b;
+ uint8_t p[9] = {0x01};
+
+ l_put_be16(frnd->dst, p + 1);
+ l_put_be16(frnd->net->src_addr, p + 3);
+ l_put_be16(frnd->lp_cnt, p + 5);
+ l_put_be16(frnd->fn_cnt, p + 7);
+
+ mesh_crypto_k2(key, p, sizeof(p), &frnd->new_key_set.nid,
+ frnd->new_key_set.enc_key,
+ frnd->new_key_set.privacy_key);
+
+ mesh_net_add_keyset(frnd->net, &frnd->new_key_set);
+ l_info("Add New KeySet %2.2x for %4.4x",
+ frnd->new_key_set.nid, frnd->dst);
+ l_info("Outgoing with %2.2x", frnd->key_set.nid);
+}
+
+static void frnd_kr_phase2(void *a, void *b)
+{
+ struct mesh_friend *frnd = a;
+
+ /*
+ * I think that a Friend should use Old Key as long as possible
+ * Because a Friend Node will enter Phase 3 before it's LPN.
+ * Alternatively, the FN could keep the Old Friend Keys until it
+ * receives it's first Poll using the new keys (?)
+ */
+
+ l_info("Use Both KeySet %2.2x && %2.2x for %4.4x",
+ frnd->key_set.nid, frnd->new_key_set.nid, frnd->dst);
+}
+
+static void frnd_kr_phase3(void *a, void *b)
+{
+ struct mesh_friend *frnd = a;
+ struct mesh_net *net = b;
+
+ l_info("Replace KeySet %2.2x with %2.2x for %4.4x",
+ frnd->key_set.nid, frnd->new_key_set.nid, frnd->dst);
+ frnd->key_set = frnd->new_key_set;
+ mesh_net_remove_keyset(net, &frnd->new_key_set);
+ frnd->new_key_set.nid = 0xff;
+}
+
+/* TODO: add net key idx? For now, use primary net key */
+struct mesh_friend *mesh_friend_new(struct mesh_net *net, uint16_t dst,
+ uint8_t ele_cnt, uint8_t frd,
+ uint8_t frw, uint32_t fpt,
+ uint16_t fn_cnt, uint16_t lp_cnt)
+{
+ struct mesh_subnet *subnet;
+ uint8_t p[9] = {0x01};
+ struct mesh_friend *frnd = l_queue_find(net->friends,
+ match_by_friend, L_UINT_TO_PTR(dst));
+
+ if (frnd) {
+ /* Kill all timers and empty cache for this friend */
+ free_friend_internals(frnd);
+ l_timeout_remove(frnd->timeout);
+ frnd->timeout = NULL;
+ } else {
+ frnd = l_new(struct mesh_friend, 1);
+ l_queue_push_head(net->friends, frnd);
+ }
+
+ /* add _k2 */
+ frnd->net = net;
+ frnd->dst = dst;
+ frnd->frd = frd;
+ frnd->frw = frw;
+ frnd->fn_cnt = fn_cnt;
+ frnd->lp_cnt = lp_cnt;
+ frnd->poll_timeout = fpt;
+ frnd->ele_cnt = ele_cnt;
+ frnd->pkt_cache = l_queue_new();
+ frnd->new_key_set.nid = NET_NID_INVALID;
+
+ l_put_be16(dst, p + 1);
+ l_put_be16(net->src_addr, p + 3);
+ l_put_be16(lp_cnt, p + 5);
+ l_put_be16(fn_cnt, p + 7);
+
+ subnet = get_primary_subnet(net);
+ /* TODO: the primary key must be present, do we need to add check?. */
+
+ mesh_crypto_k2(subnet->current.key, p, sizeof(p),
+ &frnd->key_set.nid,
+ frnd->key_set.enc_key,
+ frnd->key_set.privacy_key);
+
+ frnd->key_set.frnd = true;
+ mesh_net_add_keyset(net, &frnd->key_set);
+
+ if (subnet->updated.key_set.nid == NET_NID_INVALID)
+ return frnd;
+
+ mesh_crypto_k2(subnet->updated.key, p, sizeof(p),
+ &frnd->new_key_set.nid,
+ frnd->new_key_set.enc_key,
+ frnd->new_key_set.privacy_key);
+ frnd->new_key_set.frnd = true;
+ mesh_net_add_keyset(net, &frnd->new_key_set);
+
+ return frnd;
+}
+
+void mesh_friend_free(void *data)
+{
+ struct mesh_friend *frnd = data;
+
+ free_friend_internals(frnd);
+ l_timeout_remove(frnd->timeout);
+ l_free(frnd);
+}
+
+bool mesh_friend_clear(struct mesh_net *net, struct mesh_friend *frnd)
+{
+ bool removed = l_queue_remove(net->friends, frnd);
+
+ free_friend_internals(frnd);
+
+ return removed;
+}
+
+bool mesh_net_add_keyset(struct mesh_net *net, struct mesh_key_set *key_set)
+{
+ if (!net)
+ return false;
+
+ l_info("Add KEY_SET %2.2x (%d) %p",
+ key_set->nid, key_set->frnd, key_set);
+ return l_queue_push_tail(net->key_sets, key_set);
+}
+
+bool mesh_net_remove_keyset(struct mesh_net *net, struct mesh_key_set *key_set)
+{
+ if (!net || !net->key_sets)
+ return false;
+
+ l_info("DEL KEY_SET %2.2x (%d) %p",
+ key_set->nid, key_set->frnd, key_set);
+ return l_queue_remove(net->key_sets, key_set);
+}
+
+void mesh_friend_sub_add(struct mesh_net *net, uint16_t lpn, uint8_t ele_cnt,
+ uint8_t grp_cnt,
+ const uint8_t *list)
+{
+ uint16_t *new_list;
+ uint16_t *grp_list;
+ struct mesh_friend *frnd = l_queue_find(net->friends,
+ match_by_friend,
+ L_UINT_TO_PTR(lpn));
+ if (!frnd)
+ return;
+
+ new_list = l_malloc((grp_cnt + frnd->grp_cnt) * sizeof(uint16_t));
+ grp_list = frnd->grp_list;
+
+ if (grp_list && frnd->grp_cnt)
+ memcpy(new_list, grp_list, frnd->grp_cnt * sizeof(uint16_t));
+
+ memcpy(&new_list[frnd->grp_cnt], list, grp_cnt * sizeof(uint16_t));
+ l_free(grp_list);
+ frnd->ele_cnt = ele_cnt;
+ frnd->grp_list = new_list;
+ frnd->grp_cnt += grp_cnt;
+}
+
+void mesh_friend_sub_del(struct mesh_net *net, uint16_t lpn,
+ uint8_t cnt,
+ const uint8_t *del_list)
+{
+ uint16_t *grp_list;
+ int16_t i, grp_cnt;
+ size_t cnt16 = cnt * sizeof(uint16_t);
+ struct mesh_friend *frnd = l_queue_find(net->friends,
+ match_by_friend,
+ L_UINT_TO_PTR(lpn));
+ if (!frnd)
+ return;
+
+ grp_cnt = frnd->grp_cnt;
+ grp_list = frnd->grp_list;
+
+ while (cnt-- && grp_cnt) {
+ cnt16 -= sizeof(uint16_t);
+ for (i = grp_cnt - 1; i >= 0; i--) {
+ if (l_get_le16(del_list + cnt16) == grp_list[i]) {
+ grp_cnt--;
+ memcpy(&grp_list[i], &grp_list[i + 1],
+ (grp_cnt - i) * sizeof(uint16_t));
+ break;
+ }
+ }
+ }
+
+ frnd->grp_cnt = grp_cnt;
+
+ if (!grp_cnt) {
+ l_free(frnd->grp_list);
+ frnd->grp_list = NULL;
+ }
+}
+
+uint32_t mesh_net_next_seq_num(struct mesh_net *net)
+{
+ uint32_t seq = net->seq_num;
+
+ net->seq_num++;
+
+ /* 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);
+ }
+
+ return seq;
+}
+
+static struct mesh_sar *mesh_sar_new(size_t len)
+{
+ size_t size = sizeof(struct mesh_sar) + len;
+ struct mesh_sar *sar;
+
+ sar = l_malloc(size);
+
+ memset(sar, 0, size);
+
+ return sar;
+}
+
+static void mesh_sar_free(void *data)
+{
+ struct mesh_sar *sar = data;
+
+ if (!sar)
+ return;
+
+ l_timeout_remove(sar->seg_timeout);
+ l_timeout_remove(sar->msg_timeout);
+ l_free(sar);
+}
+
+static void mesh_msg_free(void *data)
+{
+ struct mesh_msg *msg = data;
+
+ l_free(msg);
+}
+
+static void lpn_process_beacon(void *user_data, const void *data, uint8_t size,
+ int8_t rssi);
+
+static struct mesh_subnet *subnet_new(struct mesh_net *net, uint16_t idx)
+{
+ struct mesh_subnet *subnet;
+
+ subnet = l_new(struct mesh_subnet, 1);
+ if (!subnet)
+ return NULL;
+
+ subnet->net = net;
+ subnet->idx = idx;
+ subnet->tx = &subnet->current;
+ subnet->updated.key_set.nid = NET_NID_INVALID;
+ subnet->snb.beacon[0] = MESH_AD_TYPE_BEACON;
+ return subnet;
+}
+
+static bool create_keys(struct mesh_net *net, struct net_key *keys,
+ const uint8_t *net_key)
+{
+ uint8_t nid[1];
+ uint8_t enc_key[16];
+ uint8_t privacy_key[16];
+ uint8_t network_id[8];
+ uint8_t p[] = {0};
+
+ if (!mesh_crypto_k2(net_key, p, sizeof(p),
+ nid, enc_key, privacy_key))
+ return false;
+
+ if (!mesh_crypto_k3(net_key, network_id))
+ return false;
+
+ if (!mesh_crypto_nkbk(net_key, keys->beacon_key))
+ return false;
+
+ keys->key_set.frnd = false;
+ keys->key_set.nid = nid[0];
+ memcpy(keys->key_set.enc_key, enc_key, 16);
+ memcpy(keys->key_set.privacy_key, privacy_key, 16);
+ memcpy(keys->network_id, network_id, 8);
+ memcpy(keys->key, net_key, 16);
+ return true;
+}
+
+static bool create_secure_beacon(struct mesh_net *net,
+ struct mesh_subnet *subnet,
+ uint8_t *beacon_data, uint8_t size)
+{
+ uint64_t cmac;
+
+ if (size < 22)
+ return false;
+
+ beacon_data[0] = BEACON_TYPE_SNB;
+ beacon_data[1] = 0;
+
+ if (subnet->key_refresh)
+ beacon_data[1] |= 0x01;
+
+ if (iv_is_updating(net))
+ beacon_data[1] |= 0x02;
+
+ memcpy(beacon_data + 2, subnet->tx->network_id, 8);
+ l_put_be32(net->iv_index, beacon_data + 10);
+
+ if (!mesh_crypto_beacon_cmac(subnet->tx->beacon_key,
+ subnet->tx->network_id,
+ net->iv_index, subnet->key_refresh,
+ iv_is_updating(net), &cmac))
+ return false;
+
+ l_put_be64(cmac, beacon_data + 14);
+
+ return true;
+}
+
+static void send_network_beacon(struct mesh_subnet *subnet,
+ struct mesh_net *net)
+{
+ struct mesh_io_send_info info = {
+ .type = MESH_IO_TIMING_TYPE_GENERAL,
+ .u.gen.interval = net->tx_interval,
+ .u.gen.cnt = 1,
+ .u.gen.min_delay = DEFAULT_MIN_DELAY,
+ .u.gen.max_delay = DEFAULT_MAX_DELAY
+ };
+
+ l_info("Send SNB on network %3.3x", subnet->idx);
+ mesh_io_send(net->io, &info, subnet->snb.beacon,
+ sizeof(subnet->snb.beacon));
+}
+
+static void network_beacon_timeout(struct l_timeout *timeout, void *user_data)
+{
+ struct mesh_subnet *subnet = user_data;
+ uint32_t interval;
+
+ send_network_beacon(subnet, subnet->net);
+
+ if (!subnet->snb.half_period) {
+ l_debug("beacon TO period %d, observed %d, expected %d",
+ subnet->snb.observe_period,
+ subnet->snb.observed,
+ subnet->snb.expected);
+ interval = subnet->snb.observe_period *
+ (subnet->snb.observed + 1) / subnet->snb.expected;
+ subnet->snb.observe_period = interval * 2;
+ subnet->snb.expected = subnet->snb.observe_period / 10;
+ subnet->snb.observed = 0;
+ } else
+ interval = subnet->snb.observe_period / 2;
+
+ if (interval < BEACON_INTERVAL_MIN)
+ interval = BEACON_INTERVAL_MIN;
+
+ if (interval > BEACON_INTERVAL_MAX)
+ interval = BEACON_INTERVAL_MAX;
+
+ subnet->snb.ts = get_timestamp_secs();
+ subnet->snb.half_period ^= 1;
+ l_timeout_modify(timeout, interval);
+}
+
+static void start_network_beacon(void *a, void *b)
+{
+ struct mesh_subnet *subnet = a;
+ struct mesh_net *net = b;
+
+ if (!net->beacon_enable) {
+ if (subnet->snb.timeout)
+ l_timeout_remove(subnet->snb.timeout);
+ subnet->snb.timeout = NULL;
+ return;
+ }
+
+ /* If timeout is active, let it run it's course */
+ if (subnet->snb.timeout)
+ return;
+
+ send_network_beacon(subnet, subnet->net);
+
+ subnet->snb.ts = get_timestamp_secs();
+ subnet->snb.expected = 2;
+ subnet->snb.observed = 0;
+ subnet->snb.half_period = 1;
+ subnet->snb.observe_period = BEACON_INTERVAL_MIN * 2;
+
+ subnet->snb.timeout = l_timeout_create(BEACON_INTERVAL_MIN,
+ network_beacon_timeout, subnet, NULL);
+}
+
+struct mesh_net *mesh_net_new(uint16_t index)
+{
+ struct mesh_net *net;
+
+ net = l_new(struct mesh_net, 1);
+
+ if (!net)
+ return NULL;
+
+ net->pkt_id = 0;
+ net->bea_id = 0;
+ net->key_id_next = 0;
+
+ net->beacon_enable = true;
+ net->proxy_enable = false;
+ net->relay.enable = false;
+
+ net->seq_num = 0x000000;
+ net->src_addr = 0x0000;
+ net->default_ttl = 0x00;
+
+ net->provisioner = false;
+
+ net->test_mode = false;
+ memset(&net->prov_caps, 0, sizeof(net->prov_caps));
+ net->prov_caps.algorithms = 1;
+
+ net->tx_cnt = DEFAULT_TRANSMIT_COUNT;
+ net->tx_interval = DEFAULT_TRANSMIT_INTERVAL;
+
+ net->subnets = l_queue_new();
+ net->key_sets = l_queue_new();
+ net->fast_cache = l_queue_new();
+ net->msg_cache = l_queue_new();
+ net->sar_in = l_queue_new();
+ net->sar_out = l_queue_new();
+ net->frnd_msgs = l_queue_new();
+ net->friends = l_queue_new();
+ net->destinations = l_queue_new();
+ net->app_keys = l_queue_new();
+
+ 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);
+
+ return net;
+}
+
+void mesh_net_unref(struct mesh_net *net)
+{
+ if (!net)
+ return;
+
+ if (__sync_sub_and_fetch(&net->ref_count, 1))
+ return;
+
+ /* key_sets are not allocated to this queue. Only Borrowed */
+ l_queue_destroy(net->key_sets, NULL);
+ net->key_sets = NULL;
+
+ l_queue_destroy(net->subnets, l_free);
+ l_queue_destroy(net->fast_cache, mesh_msg_free);
+ l_queue_destroy(net->msg_cache, mesh_msg_free);
+ l_queue_destroy(net->sar_in, mesh_sar_free);
+ l_queue_destroy(net->sar_out, mesh_sar_free);
+ l_queue_destroy(net->frnd_msgs, l_free);
+ l_queue_destroy(net->friends, mesh_friend_free);
+ l_queue_destroy(net->destinations, l_free);
+ l_queue_destroy(net->app_keys, appkey_key_free);
+
+ l_free(net);
+}
+
+int mesh_net_del_key(struct mesh_net *net, uint16_t idx)
+{
+ struct mesh_subnet *subnet;
+
+ if (!net)
+ return MESH_STATUS_UNSPECIFIED_ERROR;
+
+ /* Cannot remove primary key */
+ if (l_queue_length(net->subnets) <= 1)
+ return MESH_STATUS_CANNOT_REMOVE;
+
+ subnet = l_queue_find(net->subnets, match_key_index,
+ L_UINT_TO_PTR(idx));
+ if (!subnet)
+ return MESH_STATUS_CANNOT_REMOVE;
+
+ /* Delete associated app keys */
+ appkey_delete_bound_keys(net, idx);
+
+ /* Disable hearbeat publication on this subnet */
+ if (idx == net->heartbeat.pub_net_idx)
+ net->heartbeat.pub_dst = UNASSIGNED_ADDRESS;
+
+ mesh_net_remove_keyset(net, &subnet->current.key_set);
+ mesh_net_remove_keyset(net, &subnet->updated.key_set);
+
+ /* TODO: cancel beacon_enable on this subnet */
+
+ l_queue_remove(net->subnets, subnet);
+ if (!storage_local_net_key_del(net, idx))
+ return MESH_STATUS_STORAGE_FAIL;
+
+ return MESH_STATUS_SUCCESS;
+}
+
+int mesh_net_add_key(struct mesh_net *net, bool update, uint16_t idx,
+ const void *value)
+{
+ int status;
+ struct mesh_subnet *subnet;
+
+ subnet = l_queue_find(net->subnets, match_key_index,
+ L_UINT_TO_PTR(idx));
+
+ if (update) {
+ if (subnet && subnet->kr_phase == KEY_REFRESH_PHASE_NONE) {
+ 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,
+ value, KEY_REFRESH_PHASE_ONE))
+ return MESH_STATUS_STORAGE_FAIL;
+ } else
+ return MESH_STATUS_CANNOT_UPDATE;
+ }
+
+ if (subnet)
+ return memcmp(subnet->current.key, value, 16) ?
+ MESH_STATUS_IDX_ALREADY_STORED : MESH_STATUS_SUCCESS;
+
+ subnet = subnet_new(net, idx);
+ if (!subnet)
+ return MESH_STATUS_INSUFF_RESOURCES;
+
+ if (!create_keys(net, &subnet->current, value) ||
+ !mesh_net_add_keyset(net, &subnet->current.key_set)) {
+ l_free(subnet);
+ return MESH_STATUS_INSUFF_RESOURCES;
+ }
+
+ if (!create_secure_beacon(net, subnet, &subnet->snb.beacon[1], 22) ||
+ !l_queue_push_tail(net->subnets, subnet)) {
+ mesh_net_remove_keyset(net, &subnet->current.key_set);
+ l_free(subnet);
+ return MESH_STATUS_INSUFF_RESOURCES;
+ }
+
+ if (!storage_local_net_key_add(net, idx, value,
+ KEY_REFRESH_PHASE_NONE)) {
+ l_queue_remove(net->subnets, subnet);
+ mesh_net_remove_keyset(net, &subnet->current.key_set);
+ l_free(subnet);
+ return MESH_STATUS_STORAGE_FAIL;
+ }
+
+ start_network_beacon(subnet, net);
+
+ return MESH_STATUS_SUCCESS;
+}
+
+void mesh_net_flush_msg_queues(struct mesh_net *net)
+{
+ l_queue_clear(net->msg_cache, mesh_msg_free);
+ l_queue_clear(net->fast_cache, mesh_msg_free);
+}
+
+static bool match_cache(const void *a, const void *b)
+{
+ const struct mesh_msg *msg = a;
+ const struct mesh_msg *tst = b;
+
+ if (msg->seq != tst->seq || msg->mic != tst->mic ||
+ msg->src != tst->src)
+ return false;
+
+ return true;
+}
+
+static bool msg_in_cache(struct mesh_net *net, uint16_t src, uint32_t seq,
+ uint32_t mic)
+{
+ struct mesh_msg *msg;
+ struct mesh_msg tst = {
+ .src = src,
+ .seq = seq,
+ .mic = mic,
+ };
+
+ msg = l_queue_remove_if(net->msg_cache, match_cache, &tst);
+
+ if (msg) {
+ l_debug("Supressing duplicate %4.4x + %6.6x + %8.8x",
+ src, seq, mic);
+ l_queue_push_head(net->msg_cache, msg);
+ return true;
+ }
+
+ msg = l_new(struct mesh_msg, 1);
+ *msg = tst;
+ l_queue_push_head(net->msg_cache, msg);
+ l_debug("Add %4.4x + %6.6x + %8.8x", src, seq, mic);
+
+ if (l_queue_length(net->msg_cache) > MSG_CACHE_SIZE) {
+ msg = l_queue_peek_tail(net->msg_cache);
+ /* Remove Tail (oldest msg in cache) */
+ l_debug("Remove %4.4x + %6.6x + %8.8x",
+ msg->src, msg->seq, msg->mic);
+ if (l_queue_remove(net->msg_cache, msg))
+ l_free(msg);
+ }
+
+ return false;
+}
+
+static bool match_sar_seq0(const void *a, const void *b)
+{
+ const struct mesh_sar *sar = a;
+ uint16_t seqZero = L_PTR_TO_UINT(b);
+
+ return sar->seqZero == seqZero;
+}
+
+static bool match_sar_remote(const void *a, const void *b)
+{
+ const struct mesh_sar *sar = a;
+ uint16_t remote = L_PTR_TO_UINT(b);
+
+ return sar->remote == remote;
+}
+
+static bool match_msg_timeout(const void *a, const void *b)
+{
+ const struct mesh_sar *sar = a;
+ const struct l_timeout *msg_timeout = 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;
+ const struct l_timeout *seg_timeout = b;
+
+ return sar->seg_timeout == seg_timeout;
+}
+
+static bool match_dest_dst(const void *a, const void *b)
+{
+ const struct mesh_destination *dest = a;
+ uint16_t dst = L_PTR_TO_UINT(b);
+
+ return dst == dest->dst;
+}
+
+static bool match_frnd_dst(const void *a, const void *b)
+{
+ const struct mesh_friend *frnd = a;
+ uint16_t dst = L_PTR_TO_UINT(b);
+ int16_t i, grp_cnt = frnd->grp_cnt;
+ uint16_t *grp_list = frnd->grp_list;
+
+ /*
+ * Determine if this message is for this friends unicast
+ * address, and/or one of it's group/virtual addresses
+ */
+ if (dst >= frnd->dst && dst < (frnd->dst + frnd->ele_cnt))
+ return true;
+
+ if (!(dst & 0x8000))
+ return false;
+
+ for (i = 0; i < grp_cnt; i++) {
+ if (dst == grp_list[i])
+ return true;
+ }
+
+ return false;
+}
+
+static bool is_lpn_friend(struct mesh_net *net, uint16_t addr, bool frnd)
+{
+ void *tst;
+
+ if (!frnd)
+ return false;
+
+ tst = l_queue_find(net->friends, match_frnd_dst, L_UINT_TO_PTR(addr));
+
+ return tst != NULL;
+}
+
+static bool is_us(struct mesh_net *net, uint16_t addr, bool src)
+{
+ void *tst;
+
+ if (IS_ALL_NODES(addr))
+ return true;
+
+ if (addr == FRIENDS_ADDRESS)
+ return net->friend_enable;
+
+ if (addr == RELAYS_ADDRESS)
+ return net->relay.enable;
+
+ if (addr == PROXIES_ADDRESS)
+ return net->proxy_enable;
+
+ if (addr >= net->src_addr && addr <= net->last_addr)
+ return true;
+
+ tst = l_queue_find(net->destinations, match_dest_dst,
+ L_UINT_TO_PTR(addr));
+
+ if (tst == NULL && !src)
+ tst = l_queue_find(net->friends, match_frnd_dst,
+ L_UINT_TO_PTR(addr));
+
+ return tst != NULL;
+}
+
+static struct mesh_friend_msg *mesh_friend_msg_new(uint8_t seg_max)
+{
+ struct mesh_friend_msg *frnd_msg;
+
+ if (seg_max) {
+ size_t size = sizeof(struct mesh_friend_msg) -
+ sizeof(struct mesh_friend_seg_one);
+
+ size += (seg_max + 1) * sizeof(struct mesh_friend_seg_12);
+ frnd_msg = (struct mesh_friend_msg *) l_new(uint8_t, size);
+ } else
+ frnd_msg = l_new(struct mesh_friend_msg, 1);
+
+
+ return frnd_msg;
+}
+
+
+static bool match_ack(const void *a, const void *b)
+{
+ const struct mesh_friend_msg *old = a;
+ const struct mesh_friend_msg *rx = b;
+ uint32_t old_hdr;
+ uint32_t new_hdr;
+
+ /* Determine if old pkt is ACK to same SAR message that new ACK is */
+ if (!old->ctl || old->src != rx->src)
+ return false;
+
+ /* Check the quickest items first before digging deeper */
+ old_hdr = old->u.one[0].hdr & HDR_ACK_MASK;
+ new_hdr = rx->u.one[0].hdr & HDR_ACK_MASK;
+
+ return old_hdr == new_hdr;
+}
+
+static void enqueue_friend_pkt(void *a, void *b)
+{
+ struct mesh_friend *frnd = a;
+ struct mesh_friend_msg *pkt, *rx = b;
+ size_t size;
+ int16_t i;
+
+ if (rx->done)
+ return;
+
+ /*
+ * Determine if this message is for this friends unicast
+ * address, and/or one of it's group/virtual addresses
+ */
+ if (rx->dst >= frnd->dst && (rx->dst - frnd->dst) < frnd->ele_cnt) {
+ rx->done = true;
+ goto enqueue;
+ }
+
+ if (!(rx->dst & 0x8000))
+ return;
+
+ if (!IS_ALL_NODES(rx->dst)) {
+ for (i = 0; i < frnd->grp_cnt; i++) {
+ if (rx->dst == frnd->grp_list[i])
+ goto enqueue;
+ }
+ return;
+ }
+
+enqueue:
+ /* Special handling for Seg Ack -- Only one per message queue */
+ if (((rx->u.one[0].hdr >> OPCODE_HDR_SHIFT) & OPCODE_MASK) ==
+ NET_OP_SEG_ACKNOWLEDGE) {
+ void *old_head = l_queue_peek_head(frnd->pkt_cache);
+ /* Suppress duplicate ACKs */
+ do {
+ void *old = l_queue_remove_if(frnd->pkt_cache,
+ match_ack, rx);
+
+ if (old) {
+ if (old_head == old) {
+ /*
+ * If we are discarding head for any
+ * reason, reset FRND SEQ
+ */
+ frnd->last = frnd->seq;
+ }
+
+ l_free(old);
+ } else
+ break;
+
+ } while (true);
+ }
+
+ l_debug("%s for %4.4x from %4.4x ttl: %2.2x (seq: %6.6x) (ctl: %d)",
+ __func__, frnd->dst, rx->src, rx->ttl,
+ rx->u.one[0].seq, rx->ctl);
+
+ if (rx->cnt_in) {
+ size = sizeof(struct mesh_friend_msg) -
+ sizeof(struct mesh_friend_seg_one);
+ size += (rx->cnt_in + 1) * sizeof(struct mesh_friend_seg_12);
+ } else
+ size = sizeof(struct mesh_friend_msg);
+
+ pkt = l_malloc(size);
+ memcpy(pkt, rx, size);
+
+ l_queue_push_tail(frnd->pkt_cache, pkt);
+
+ if (l_queue_length(frnd->pkt_cache) > FRND_CACHE_MAX) {
+ /*
+ * TODO: Guard against popping UPDATE packets
+ * (disallowed per spec)
+ */
+ pkt = l_queue_pop_head(frnd->pkt_cache);
+ l_free(pkt);
+ frnd->last = frnd->seq;
+ }
+}
+
+static void enqueue_update(void *a, void *b)
+{
+ struct mesh_friend *frnd = a;
+ struct mesh_friend_msg *pkt = b;
+
+ pkt->dst = frnd->dst;
+ pkt->done = false;
+ enqueue_friend_pkt(frnd, pkt);
+}
+
+static uint32_t seq_auth(uint32_t seq, uint16_t seqZero)
+{
+ uint32_t seqAuth = seqZero & SEQ_ZERO_MASK;
+
+ seqAuth |= seq & (~SEQ_ZERO_MASK);
+ if (seqAuth > seq)
+ seqAuth -= (SEQ_ZERO_MASK + 1);
+
+ return seqAuth;
+}
+
+static bool friend_packet_queue(struct mesh_net *net,
+ uint32_t iv_index,
+ bool ctl, uint8_t ttl,
+ uint32_t seq,
+ uint16_t src, uint16_t dst,
+ uint32_t hdr,
+ const uint8_t *data, uint16_t size)
+{
+ struct mesh_friend_msg *frnd_msg;
+ uint8_t seg_max = SEG_TOTAL(hdr);
+ bool ret;
+
+ if (seg_max && !IS_SEGMENTED(hdr))
+ return false;
+
+ frnd_msg = mesh_friend_msg_new(seg_max);
+
+ if (IS_SEGMENTED(hdr)) {
+ uint32_t seqAuth = seq_auth(seq, hdr >> SEQ_ZERO_HDR_SHIFT);
+ uint8_t i;
+
+ for (i = 0; i <= seg_max; i++) {
+ memcpy(frnd_msg->u.s12[i].data, data, 12);
+ frnd_msg->u.s12[i].hdr = hdr;
+ frnd_msg->u.s12[i].seq = seqAuth + i;
+ data += 12;
+ hdr += (1 << SEGO_HDR_SHIFT);
+ }
+ frnd_msg->u.s12[seg_max].seq = seq;
+ frnd_msg->cnt_in = seg_max;
+ frnd_msg->last_len = size % 12;
+ if (!frnd_msg->last_len)
+ frnd_msg->last_len = 12;
+ } else {
+ uint8_t opcode = hdr >> OPCODE_HDR_SHIFT;
+
+ if (ctl && opcode != NET_OP_SEG_ACKNOWLEDGE) {
+
+ /* Don't cache Friend Ctl opcodes */
+ if (FRND_OPCODE(opcode)) {
+ l_free(frnd_msg);
+ return false;
+ }
+
+ memcpy(frnd_msg->u.one[0].data + 1, data, size);
+ frnd_msg->last_len = size + 1;
+ frnd_msg->u.one[0].data[0] = opcode;
+ } else {
+ memcpy(frnd_msg->u.one[0].data, data, size);
+ frnd_msg->last_len = size;
+ }
+ frnd_msg->u.one[0].hdr = hdr;
+ frnd_msg->u.one[0].seq = seq;
+ }
+
+ frnd_msg->iv_index = iv_index;
+ frnd_msg->src = src;
+ frnd_msg->dst = dst;
+ frnd_msg->ctl = ctl;
+ frnd_msg->ttl = ttl;
+
+ /* Re-Package into Friend Delivery payload */
+ l_queue_foreach(net->friends, enqueue_friend_pkt, frnd_msg);
+ ret = frnd_msg->done;
+
+ /* TODO Optimization(?): Unicast messages keep this buffer */
+ l_free(frnd_msg);
+
+ return ret;
+}
+
+static void friend_ack_rxed(struct mesh_net *net, uint32_t iv_index,
+ uint32_t seq,
+ uint16_t src, uint16_t dst,
+ const uint8_t *pkt)
+{
+ uint32_t hdr = l_get_be32(pkt) &
+ ((SEQ_ZERO_MASK << SEQ_ZERO_HDR_SHIFT) | /* Preserve SeqZero */
+ (true << RELAY_HDR_SHIFT)); /* Preserve Relay bit */
+ uint32_t flags = l_get_be32(pkt + 3);
+ struct mesh_friend_msg frnd_ack = {
+ .ctl = true,
+ .iv_index = iv_index,
+ .src = src,
+ .dst = dst,
+ .last_len = sizeof(flags),
+ .u.one[0].seq = seq,
+ .done = false,
+ };
+
+ hdr |= NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT;
+ frnd_ack.u.one[0].hdr = hdr;
+ l_put_be32(flags, frnd_ack.u.one[0].data);
+ l_queue_foreach(net->friends, enqueue_friend_pkt, &frnd_ack);
+}
+
+static bool send_seg(struct mesh_net *net, struct mesh_sar *msg, uint8_t seg);
+
+static void send_frnd_ack(struct mesh_net *net, uint16_t src, uint16_t dst,
+ uint32_t hdr, uint32_t flags)
+{
+ uint32_t expected;
+ uint8_t msg[7];
+
+ /* We don't ACK from multicast destinations */
+ if (src & 0x8000)
+ return;
+
+ /* Calculate the "Full ACK" mask */
+ expected = 0xffffffff >> (31 - SEG_TOTAL(hdr));
+
+ /* Clear Hdr bits that don't apply to Seg ACK */
+ hdr &= ~((true << SEG_HDR_SHIFT) |
+ (OPCODE_MASK << OPCODE_HDR_SHIFT) |
+ (true << SZMIC_HDR_SHIFT) |
+ (SEG_MASK << SEGO_HDR_SHIFT) |
+ (SEG_MASK << SEGN_HDR_SHIFT));
+
+ hdr |= NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT;
+ hdr |= true << RELAY_HDR_SHIFT;
+
+ /* Clear all unexpected bits */
+ flags &= expected;
+
+ l_put_be32(hdr, msg);
+ l_put_be32(flags, msg + 3);
+
+ l_info("Send Friend ACK to Segs: %8.8x", flags);
+
+ if (is_lpn_friend(net, dst, true)) {
+ /* If we are acking our LPN Friend, queue, don't send */
+ friend_ack_rxed(net, mesh_net_get_iv_index(net),
+ mesh_net_next_seq_num(net), 0, dst, msg);
+ } else {
+ mesh_net_transport_send(net, NULL, false,
+ mesh_net_get_iv_index(net), DEFAULT_TTL,
+ 0, 0, dst, msg, sizeof(msg));
+ }
+}
+
+static void send_net_ack(struct mesh_net *net, struct mesh_sar *sar,
+ uint32_t flags)
+{
+ uint8_t msg[7];
+ uint32_t hdr;
+ uint16_t src = sar->src;
+ uint16_t dst = sar->remote;
+
+ /* We don't ACK from multicast destinations */
+ if (src & 0x8000)
+ return;
+
+ /* We don't ACK segments as a Low Power Node */
+ if (net->friend_addr)
+ return;
+
+ hdr = NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT;
+ hdr |= sar->seqZero << SEQ_ZERO_HDR_SHIFT;
+
+ if (is_lpn_friend(net, src, true))
+ hdr |= true << RELAY_HDR_SHIFT;
+
+ l_put_be32(hdr, msg);
+ l_put_be32(flags, msg + 3);
+ l_info("Send%s ACK to Segs: %8.8x", sar->frnd ? " Friend" : "", flags);
+
+ if (is_lpn_friend(net, dst, true)) {
+ /* If we are acking our LPN Friend, queue, don't send */
+ friend_ack_rxed(net, mesh_net_get_iv_index(net),
+ mesh_net_next_seq_num(net), src, dst, msg);
+ return;
+ }
+
+ mesh_net_transport_send(net, NULL, false,
+ mesh_net_get_iv_index(net), DEFAULT_TTL,
+ 0, src, dst, msg, sizeof(msg));
+}
+
+static void inseg_to(struct l_timeout *seg_timeout, void *user_data)
+{
+ struct mesh_net *net = user_data;
+ struct mesh_sar *sar = l_queue_find(net->sar_in,
+ match_seg_timeout, seg_timeout);
+
+ l_timeout_remove(seg_timeout);
+ if (!sar)
+ return;
+
+ /* Send NAK */
+ l_info("Timeout %p %3.3x", sar, sar->app_idx);
+ send_net_ack(net, sar, sar->flags);
+
+ sar->seg_timeout = l_timeout_create(SEG_TO, inseg_to, net, NULL);
+}
+
+static void inmsg_to(struct l_timeout *msg_timeout, void *user_data)
+{
+ struct mesh_net *net = user_data;
+ struct mesh_sar *sar = l_queue_remove_if(net->sar_in,
+ match_msg_timeout, msg_timeout);
+
+ l_timeout_remove(msg_timeout);
+ if (!sar)
+ return;
+
+ sar->msg_timeout = NULL;
+
+ /* print_packet("Incoming SAR Timeout", sar->buf, sar->len); */
+ mesh_sar_free(sar);
+}
+
+static void outmsg_to(struct l_timeout *msg_timeout, void *user_data)
+{
+ struct mesh_net *net = user_data;
+ struct mesh_sar *sar = l_queue_remove_if(net->sar_out,
+ match_msg_timeout, msg_timeout);
+
+ l_timeout_remove(msg_timeout);
+ if (!sar)
+ return;
+
+ sar->msg_timeout = NULL;
+
+ if (sar->status_func)
+ sar->status_func(sar->remote, 1,
+ sar->buf, sar->len - 4,
+ sar->user_data);
+
+ /* print_packet("Outgoing SAR Timeout", sar->buf, sar->len); */
+ mesh_sar_free(sar);
+}
+
+static void outseg_to(struct l_timeout *seg_timeout, void *user_data);
+static void ack_received(struct mesh_net *net, bool timeout,
+ uint16_t src, uint16_t dst,
+ uint16_t seq0, uint32_t ack_flag)
+{
+ struct mesh_sar *outgoing;
+ uint32_t seg_flag = 0x00000001;
+ uint32_t ack_copy = ack_flag;
+ uint16_t i;
+
+ l_info("ACK Rxed (%x) (to:%d): %8.8x", seq0, timeout, ack_flag);
+
+ outgoing = l_queue_find(net->sar_out, match_sar_seq0,
+ L_UINT_TO_PTR(seq0));
+
+ if (!outgoing) {
+ l_info("Not Found: %4.4x", seq0);
+ return;
+ }
+
+ /*
+ * TODO -- If we receive from different
+ * SRC than we are sending to, make sure the OBO flag is set
+ */
+
+ if ((!timeout && !ack_flag) ||
+ (outgoing->flags & ack_flag) == outgoing->flags) {
+ l_debug("ob_sar_removal (%x)", outgoing->flags);
+
+ /* Note: ack_flags == 0x00000000 is a remote Cancel request */
+ if (outgoing->status_func)
+ outgoing->status_func(src, ack_flag ? 0 : 1,
+ outgoing->buf,
+ outgoing->len - 4, outgoing->user_data);
+
+ l_queue_remove(net->sar_out, outgoing);
+ mesh_sar_free(outgoing);
+
+ return;
+ }
+
+ outgoing->last_nak |= ack_flag;
+
+ ack_copy &= outgoing->flags;
+
+ for (i = 0; i <= SEG_MAX(outgoing->len); i++, seg_flag <<= 1) {
+ if (seg_flag & ack_flag) {
+ l_debug("Skipping Seg %d of %d",
+ i, SEG_MAX(outgoing->len));
+ continue;
+ }
+
+ ack_copy |= seg_flag;
+
+ l_info("Resend Seg %d net:%p dst:%x app_idx:%3.3x",
+ i, net, outgoing->remote, outgoing->app_idx);
+
+ send_seg(net, outgoing, i);
+ }
+
+ l_timeout_remove(outgoing->seg_timeout);
+ outgoing->seg_timeout = l_timeout_create(SEG_TO, outseg_to, net, NULL);
+}
+
+static void outack_to(struct l_timeout *seg_timeout, void *user_data)
+{
+ struct mesh_net *net = user_data;
+ struct mesh_sar *sar = l_queue_find(net->sar_out,
+ match_seg_timeout, seg_timeout);
+
+ l_timeout_remove(seg_timeout);
+ if (!sar)
+ return;
+
+ sar->seg_timeout = NULL;
+
+ /* Re-Send missing segments by faking NAK */
+ ack_received(net, true, sar->remote, sar->src,
+ sar->seqZero, sar->last_nak);
+}
+
+static void outseg_to(struct l_timeout *seg_timeout, void *user_data)
+{
+ struct mesh_net *net = user_data;
+ struct mesh_sar *sar = l_queue_find(net->sar_out,
+ match_seg_timeout, seg_timeout);
+
+ l_timeout_remove(seg_timeout);
+ if (!sar)
+ return;
+
+ sar->seg_timeout = NULL;
+
+ if (net->friend_addr) {
+ /* We are LPN -- Poll for ACK */
+ frnd_ack_poll(net);
+ sar->seg_timeout = l_timeout_create(SEG_TO,
+ outack_to, net, NULL);
+ } else {
+ /* Re-Send missing segments by faking NACK */
+ ack_received(net, true, sar->remote, sar->src,
+ sar->seqZero, sar->last_nak);
+ }
+}
+
+static bool msg_rxed(struct mesh_net *net, bool frnd,
+ uint32_t iv_index,
+ uint8_t ttl,
+ uint32_t seq,
+ uint16_t src, uint16_t dst,
+ uint8_t key_id,
+ bool szmic, uint16_t seqZero,
+ const uint8_t *data, uint16_t size)
+{
+ uint32_t seqAuth = seq_auth(seq, seqZero);
+
+ /* Sanity check seqAuth */
+ if (seqAuth > seq)
+ return false;
+
+ /* Save un-decrypted messages for our friends */
+ if (!frnd && l_queue_length(net->friends)) {
+ uint32_t hdr = key_id << KEY_HDR_SHIFT;
+ uint8_t frnd_ttl = ttl;
+
+ /* If not from us, decrement for our hop */
+ if (src < net->src_addr || src > net->last_addr) {
+ if (frnd_ttl > 1)
+ frnd_ttl--;
+ else
+ goto not_for_friend;
+ }
+
+ if (szmic || size > 15) {
+ hdr |= true << SEG_HDR_SHIFT;
+ hdr |= szmic << SZMIC_HDR_SHIFT;
+ hdr |= (seqZero & SEQ_ZERO_MASK) << SEQ_ZERO_HDR_SHIFT;
+ hdr |= SEG_MAX(size) << SEGN_HDR_SHIFT;
+ }
+
+ if (friend_packet_queue(net, iv_index, false, frnd_ttl,
+ seq, src, dst,
+ hdr, data, size))
+ return true;
+ }
+
+not_for_friend:
+ return mesh_model_rx(net, szmic, seqAuth, seq, iv_index,
+ ttl, src, dst, key_id, data, size);
+}
+
+static bool match_frnd_sar_dst(const void *a, const void *b)
+{
+ const struct mesh_friend_msg *frnd_msg = a;
+ uint16_t dst = L_PTR_TO_UINT(b);
+
+ return frnd_msg->dst == dst;
+}
+
+static void friend_seg_rxed(struct mesh_net *net,
+ uint32_t iv_index,
+ uint8_t ttl, uint32_t seq,
+ uint16_t src, uint16_t dst, uint32_t hdr,
+ const uint8_t *data, uint8_t size)
+{
+ struct mesh_friend *frnd = NULL;
+ struct mesh_friend_msg *frnd_msg = NULL;
+ uint8_t cnt;
+ uint8_t segN = hdr & 0x1f;
+ uint8_t segO = ((hdr >> 5) & 0x1f);
+ uint32_t expected = 0xffffffff >> (31 - segN);
+ uint32_t this_seg_flag = 0x00000001 << segO;
+ uint32_t largest = (0xffffffff << segO) & expected;
+ uint32_t hdr_key = hdr & HDR_KEY_MASK;
+
+ frnd = l_queue_find(net->friends, match_frnd_dst,
+ L_UINT_TO_PTR(dst));
+ if (!frnd)
+ return;
+
+ if (frnd->last_hdr == hdr_key) {
+ /* We are no longer receiving this msg. Resend final ACK */
+ send_frnd_ack(net, dst, src, frnd->last_hdr, 0xffffffff);
+ return;
+ }
+
+ /* Check if we have a SAR-in-progress that matches incoming segment */
+ frnd_msg = l_queue_find(net->frnd_msgs, match_frnd_sar_dst,
+ L_UINT_TO_PTR(dst));
+
+ if (frnd_msg) {
+ /* Flush if SZMICN or IV Index has changed */
+ if (frnd_msg->iv_index != iv_index)
+ frnd_msg->u.s12[0].hdr = 0;
+
+ /* Flush incomplete old SAR message if it doesn't match */
+ if ((frnd_msg->u.s12[0].hdr & HDR_KEY_MASK) != hdr_key) {
+ l_queue_remove(net->frnd_msgs, frnd_msg);
+ l_free(frnd_msg);
+ frnd_msg = NULL;
+ }
+ }
+
+ if (!frnd_msg) {
+ frnd_msg = mesh_friend_msg_new(segN);
+ frnd_msg->iv_index = iv_index;
+ frnd_msg->src = src;
+ frnd_msg->dst = dst;
+ frnd_msg->ttl = ttl;
+ l_queue_push_tail(net->frnd_msgs, frnd_msg);
+ } else if (frnd_msg->flags & this_seg_flag) /* Ignore dup segs */
+ return;
+
+ cnt = frnd_msg->cnt_in;
+ frnd_msg->flags |= this_seg_flag;
+
+ frnd_msg->u.s12[cnt].hdr = hdr;
+ frnd_msg->u.s12[cnt].seq = seq;
+ memcpy(frnd_msg->u.s12[cnt].data, data, size);
+
+ /* Last segment could be short */
+ if (segN == segO)
+ frnd_msg->last_len = size;
+
+ l_info("RXed Seg %d, Flags %8.8x (cnt: %d)",
+ segO, frnd_msg->flags, cnt);
+
+ /* In reality, if one of these is true, then *both* must be true */
+ if ((cnt == segN) || (frnd_msg->flags == expected)) {
+ l_info("Full ACK");
+ send_frnd_ack(net, dst, src, hdr, frnd_msg->flags);
+
+ if (frnd_msg->ttl > 1) {
+ frnd_msg->ttl--;
+ /* Add to friends cache */
+ l_queue_foreach(net->friends,
+ enqueue_friend_pkt, frnd_msg);
+ }
+
+ /* Remove from "in progress" queue */
+ l_queue_remove(net->frnd_msgs, frnd_msg);
+
+ /* TODO Optimization(?): Unicast messages keep this buffer */
+ l_free(frnd_msg);
+ return;
+ }
+
+ /* Always ACK if this is the largest outstanding segment */
+ if ((largest & frnd_msg->flags) == largest) {
+ l_info("Partial ACK");
+ send_frnd_ack(net, dst, src, hdr, frnd_msg->flags);
+ }
+
+ frnd_msg->cnt_in++;
+}
+
+static bool seg_rxed(struct mesh_net *net, bool frnd,
+ uint32_t iv_index,
+ uint8_t ttl,
+ uint32_t seq,
+ uint16_t src, uint16_t dst,
+ uint8_t key_id,
+ bool szmic, uint16_t seqZero,
+ uint8_t segO, uint8_t segN,
+ const uint8_t *data, uint8_t size)
+{
+ struct mesh_sar *sar_in = NULL;
+ uint16_t seg_off = 0;
+ uint32_t expected, this_seg_flag, largest, seqAuth;
+ bool reset_seg_to = true;
+
+ /*
+ * DST could receive additional Segments after
+ * completing due to a lost ACK, so re-ACK and discard
+ */
+ sar_in = l_queue_find(net->sar_in, match_sar_remote,
+ L_UINT_TO_PTR(src));
+
+ /* Discard *old* incoming-SAR-in-progress if this segment newer */
+ seqAuth = seq_auth(seq, seqZero);
+ if (sar_in && (sar_in->seqAuth != seqAuth ||
+ sar_in->iv_index != iv_index)) {
+ bool newer;
+
+ if (iv_index > sar_in->iv_index)
+ newer = true;
+ else if (iv_index == sar_in->iv_index)
+ newer = seqAuth > sar_in->seqAuth;
+ else
+ newer = false;
+
+ if (newer) {
+ /* Cancel Old, start New */
+ l_queue_remove(net->sar_in, sar_in);
+ mesh_sar_free(sar_in);
+ sar_in = NULL;
+ } else
+ /* Ignore Old */
+ return false;
+ }
+
+ expected = 0xffffffff >> (31 - segN);
+
+ if (sar_in) {
+ l_info("RXed (old: %04x %06x size:%d) %d of %d",
+ seqZero, seq, size, segO, segN);
+ /* Sanity Check--> certain things must match */
+ if (SEG_MAX(sar_in->len) != segN ||
+ sar_in->key_id != key_id)
+ return false;
+
+ if (sar_in->flags == expected) {
+ /* Re-Send ACK for full msg */
+ if (!net->friend_addr)
+ send_net_ack(net, sar_in, expected);
+ return true;
+ }
+ } else {
+ uint16_t len = MAX_SEG_TO_LEN(segN);
+
+ l_info("RXed (new: %04x %06x size: %d len: %d) %d of %d",
+ seqZero, seq, size, len, segO, segN);
+ l_debug("Queue Size: %d", l_queue_length(net->sar_in));
+ sar_in = mesh_sar_new(len);
+ sar_in->seqAuth = seqAuth;
+ sar_in->iv_index = iv_index;
+ sar_in->src = dst;
+ sar_in->remote = src;
+ sar_in->seqZero = seqZero;
+ sar_in->key_id = key_id;
+ sar_in->len = len;
+ sar_in->last_seg = 0xff;
+ if (!net->friend_addr)
+ sar_in->msg_timeout = l_timeout_create(MSG_TO,
+ inmsg_to, net, NULL);
+
+ l_debug("First Seg %4.4x", sar_in->flags);
+ l_queue_push_head(net->sar_in, sar_in);
+ }
+ /* print_packet("Seg", data, size); */
+
+ seg_off = segO * MAX_SEG_LEN;
+ memcpy(sar_in->buf + seg_off, data, size);
+ this_seg_flag = 0x00000001 << segO;
+
+ /* Don't reset Seg TO or NAK if we already have this seg */
+ if (this_seg_flag & sar_in->flags)
+ reset_seg_to = false;
+
+ sar_in->flags |= this_seg_flag;
+ sar_in->ttl = ttl;
+
+ l_debug("Have Frags %4.4x", sar_in->flags);
+
+ /* Msg length only definitive on last segment */
+ if (segO == segN)
+ sar_in->len = segN * MAX_SEG_LEN + size;
+
+ if (sar_in->flags == expected) {
+ /* Got it all */
+ if (!net->friend_addr)
+ send_net_ack(net, sar_in, expected);
+
+ msg_rxed(net, frnd,
+ iv_index,
+ ttl,
+ seq,
+ sar_in->remote, dst,
+ key_id,
+ szmic, sar_in->seqZero,
+ sar_in->buf, sar_in->len);
+
+ /* Kill Inter-Seg timeout */
+ l_timeout_remove(sar_in->seg_timeout);
+ sar_in->seg_timeout = NULL;
+ return true;
+
+ } else if (reset_seg_to) {
+ /* Restart Inter-Seg Timeout */
+ l_timeout_remove(sar_in->seg_timeout);
+
+ /* if this is the largest outstanding segment, send NAK now */
+ if (!net->friend_addr) {
+ largest = (0xffffffff << segO) & expected;
+ if ((largest & sar_in->flags) == largest)
+ send_net_ack(net, sar_in, sar_in->flags);
+
+ sar_in->seg_timeout = l_timeout_create(SEG_TO,
+ inseg_to, net, NULL);
+ }
+ }
+
+ l_debug("NAK: %d expected:%08x largest:%08x flags:%08x",
+ reset_seg_to, expected, largest, sar_in->flags);
+ return false;
+}
+
+static bool ctl_received(struct mesh_net *net, bool frnd, uint32_t iv_index,
+ uint8_t ttl,
+ uint32_t seq,
+ uint16_t src, uint16_t dst,
+ uint8_t opcode, int8_t rssi,
+ const uint8_t *pkt, uint8_t len)
+{
+ uint8_t msg[12];
+ uint8_t rsp_ttl = DEFAULT_TTL;
+ uint8_t n = 0;
+
+ if (!frnd && ttl > 1) {
+ uint32_t hdr = opcode << OPCODE_HDR_SHIFT;
+ uint8_t frnd_ttl = ttl - 1;
+
+ if (friend_packet_queue(net, iv_index,
+ true, frnd_ttl,
+ seq,
+ src, dst,
+ hdr,
+ pkt, len))
+ return true;
+ }
+
+ /* Don't process other peoples Unicast destinations */
+ if (dst < 0x8000 && (dst < net->src_addr || dst > net->last_addr))
+ return false;
+
+ switch (opcode) {
+ default:
+ l_error("Unsupported Ctl Opcode: %2.2x", opcode);
+ break;
+
+ case NET_OP_FRND_POLL:
+ if (len != 1 || ttl)
+ return false;
+
+ print_packet("Rx-NET_OP_FRND_POLL", pkt, len);
+ friend_poll(net, src, !!(pkt[0]),
+ l_queue_find(net->friends,
+ match_by_friend,
+ L_UINT_TO_PTR(src)));
+ break;
+
+ case NET_OP_FRND_UPDATE:
+ if (ttl)
+ return false;
+
+ print_packet("Rx-NET_OP_FRND_UPDATE", pkt, len);
+ lpn_process_beacon(net, pkt, len, 0);
+ break;
+
+ case NET_OP_FRND_REQUEST:
+ if (!net->friend_enable)
+ return false;
+
+ if (!IS_ALL_NODES(dst) && dst != FRIENDS_ADDRESS)
+ return false;
+
+ if (len != 10 || ttl)
+ return false;
+
+ print_packet("Rx-NET_OP_FRND_REQUEST", pkt, len);
+ friend_request(net, src, pkt[0], pkt[1],
+ l_get_be32(pkt + 1) & 0xffffff,
+ l_get_be16(pkt + 5), pkt[7],
+ l_get_be16(pkt + 8), rssi);
+ break;
+
+ case NET_OP_FRND_OFFER:
+ if (len != 6 || ttl)
+ return false;
+
+ print_packet("Rx-NET_OP_FRND_OFFER", pkt, len);
+ frnd_offer(net, src, pkt[0], pkt[1], pkt[2],
+ (int8_t) pkt[3], rssi, l_get_be16(pkt + 4));
+ break;
+
+ case NET_OP_FRND_CLEAR_CONFIRM:
+ if (len != 4)
+ return false;
+
+ print_packet("Rx-NET_OP_FRND_CLEAR_CONFIRM", pkt, len);
+ friend_clear_confirm(net, src, l_get_be16(pkt),
+ l_get_be16(pkt + 2));
+ break;
+
+ case NET_OP_FRND_CLEAR:
+ if (len != 4 || dst != net->src_addr)
+ return false;
+
+ print_packet("Rx-NET_OP_FRND_CLEAR", pkt, len);
+ friend_clear(net, src, l_get_be16(pkt), l_get_be16(pkt + 2),
+ l_queue_find(net->friends,
+ match_by_friend,
+ L_UINT_TO_PTR(l_get_be16(pkt))));
+ l_info("Remaining Friends: %d", l_queue_length(net->friends));
+ break;
+
+ case NET_OP_PROXY_SUB_ADD:
+ if (ttl)
+ return false;
+
+ print_packet("Rx-NET_OP_PROXY_SUB_ADD", pkt, len);
+ friend_sub_add(net, l_queue_find(net->friends,
+ match_by_friend, L_UINT_TO_PTR(src)),
+ pkt, len);
+ break;
+
+ case NET_OP_PROXY_SUB_REMOVE:
+ if (ttl)
+ return false;
+
+ print_packet("Rx-NET_OP_PROXY_SUB_REMOVE", pkt, len);
+ friend_sub_del(net, l_queue_find(net->friends,
+ match_by_friend, L_UINT_TO_PTR(src)),
+ pkt, len);
+ break;
+
+ case NET_OP_PROXY_SUB_CONFIRM:
+ if (ttl)
+ return false;
+
+ print_packet("Rx-NET_OP_PROXY_SUB_CONFIRM", pkt, len);
+ break;
+
+ case NET_OP_HEARTBEAT:
+ if (net->heartbeat.sub_enabled &&
+ src == net->heartbeat.sub_src) {
+ uint8_t hops = pkt[0] - ttl + 1;
+
+ print_packet("Rx-NET_OP_HEARTBEAT", pkt, len);
+
+ if (net->heartbeat.sub_count != 0xffff)
+ net->heartbeat.sub_count++;
+
+ if (net->heartbeat.sub_min_hops > hops)
+ net->heartbeat.sub_min_hops = hops;
+
+ if (net->heartbeat.sub_max_hops < hops)
+ net->heartbeat.sub_max_hops = hops;
+
+ l_info("HB: cnt:%4.4x min:%2.2x max:%2.2x",
+ net->heartbeat.sub_count,
+ net->heartbeat.sub_min_hops,
+ net->heartbeat.sub_max_hops);
+ }
+ break;
+ }
+
+ if (n) {
+ mesh_net_transport_send(net, NULL, false,
+ mesh_net_get_iv_index(net), rsp_ttl,
+ 0, dst & 0x8000 ? 0 : dst, src,
+ msg, n);
+ }
+
+ return true;
+}
+
+static bool find_fast_hash(const void *a, const void *b)
+{
+ const uint64_t *entry = a;
+ const uint64_t *test = b;
+
+ return *entry == *test;
+}
+
+static void *check_fast_cache(struct mesh_net *net, uint64_t hash)
+{
+ void *found = l_queue_find(net->fast_cache, find_fast_hash, &hash);
+ uint64_t *new_hash;
+
+ if (found)
+ return NULL;
+
+ if (l_queue_length(net->fast_cache) >= 8)
+ new_hash = l_queue_pop_head(net->fast_cache);
+ else
+ new_hash = l_malloc(sizeof(hash));
+
+ *new_hash = hash;
+ l_queue_push_tail(net->fast_cache, new_hash);
+
+ return new_hash;
+}
+
+static bool match_keyset(const void *a, const void *b)
+{
+ const struct mesh_friend *frnd = a;
+ const struct mesh_key_set *key_set = b;
+
+ return (key_set == &frnd->key_set) || (key_set == &frnd->new_key_set);
+}
+
+static void try_decode(void *a, void *b)
+{
+ struct mesh_key_set *key_set = a;
+ struct net_decode *decode = b;
+ uint8_t tmp[29];
+ bool status;
+
+ if (decode->key_set || key_set->nid != decode->nid)
+ return;
+
+ status = mesh_crypto_packet_decode(decode->packet, decode->size,
+ decode->proxy, tmp, decode->iv_index,
+ key_set->enc_key, key_set->privacy_key);
+
+ if (!status)
+ return;
+
+ memcpy(decode->packet, tmp, decode->size);
+ decode->key_set = key_set;
+ if (key_set->frnd)
+ decode->frnd = l_queue_find(decode->net->friends,
+ match_keyset, key_set);
+ else
+ decode->frnd = NULL;
+}
+
+static struct mesh_key_set *net_packet_decode(struct mesh_net *net,
+ uint32_t iv_index, uint8_t nid,
+ struct mesh_friend **frnd,
+ bool proxy,
+ uint8_t *packet, uint8_t size)
+{
+ struct net_decode decode = {
+ .net = net,
+ .key_set = NULL,
+ .nid = nid,
+ .iv_index = iv_index,
+ .packet = packet,
+ .size = size,
+ .proxy = proxy,
+ };
+
+ l_queue_foreach(net->key_sets, try_decode, &decode);
+
+ if (decode.key_set != NULL) {
+ *frnd = decode.frnd;
+ return decode.key_set;
+ }
+ return NULL;
+}
+
+static bool match_key_nid(const void *a, const void *b)
+{
+ const struct mesh_key_set *key_set = a;
+ uint8_t nid = L_PTR_TO_UINT(b);
+
+ return key_set->nid == nid;
+}
+
+static bool match_by_dst(const void *a, const void *b)
+{
+ const struct mesh_destination *dest = a;
+ uint16_t dst = L_PTR_TO_UINT(b);
+
+ return dest->dst == dst;
+}
+
+static void send_relay_pkt(struct mesh_net *net, uint8_t *packet, uint8_t size)
+{
+ struct mesh_io *io = net->io;
+ struct mesh_io_send_info info = {
+ .type = MESH_IO_TIMING_TYPE_GENERAL,
+ .u.gen.interval = net->relay.interval,
+ .u.gen.cnt = net->relay.count,
+ .u.gen.min_delay = DEFAULT_MIN_DELAY,
+ .u.gen.max_delay = DEFAULT_MAX_DELAY
+ };
+
+ packet[0] = MESH_AD_TYPE_NETWORK;
+
+ mesh_io_send(io, &info, packet, size);
+}
+
+static void send_msg_pkt(struct mesh_net *net, uint8_t *packet, uint8_t size)
+{
+ struct mesh_io *io = net->io;
+ struct mesh_io_send_info info = {
+ .type = MESH_IO_TIMING_TYPE_GENERAL,
+ .u.gen.interval = net->tx_interval,
+ .u.gen.cnt = net->tx_cnt,
+ .u.gen.min_delay = DEFAULT_MIN_DELAY,
+ /* No extra randomization when sending regular mesh messages */
+ .u.gen.max_delay = DEFAULT_MIN_DELAY
+ };
+
+ packet[0] = MESH_AD_TYPE_NETWORK;
+
+ mesh_io_send(io, &info, packet, size);
+}
+
+static void packet_received(void *user_data, const void *data, uint8_t size,
+ int8_t rssi)
+{
+ struct mesh_net *net = user_data;
+ uint32_t iv_index;
+ uint8_t iv_flag;
+ uint8_t nid;
+ const uint8_t *msg = data;
+ uint8_t app_msg_len;
+ uint8_t net_ttl, net_key_id, net_segO, net_segN, net_opcode;
+ uint32_t net_seq, cache_cookie;
+ uint16_t net_src, net_dst, net_seqZero;
+ uint8_t packet[31];
+ bool net_ctl, net_segmented, net_szmic, net_relay;
+ struct mesh_friend *net_frnd;
+ bool drop = false;
+ uint64_t hash, *isNew = NULL;
+ struct mesh_key_set *keys;
+
+ nid = msg[0] & 0x7f;
+
+ /* Ignore unrecognized NIDs */
+ if (!(l_queue_find(net->key_sets, match_key_nid, L_UINT_TO_PTR(nid)))) {
+ /* print_packet("Nope", data, size); */
+ return;
+ }
+
+ iv_flag = msg[0] >> 7;
+ iv_index = net->iv_index;
+ l_debug("%s iv_index %d NID: %2.2x", __func__, iv_index, nid);
+
+ if (sizeof(uint16_t) <= sizeof(void *)) {
+ /* Add in additional cache to allow us to
+ * avoid decrypting duplicatesr
+ * Fast 64 bit Hash, Network MIC doesn't matter
+ * With 64 bit hash, false pos chance is 1 in 1.8 * 10^19
+ */
+ hash = l_get_le64(msg + 1) ^ l_get_le64(msg + 9);
+ isNew = check_fast_cache(net, hash);
+ if (!isNew)
+ return;
+
+ l_debug("New");
+ }
+
+ memcpy(packet + 2, data, size);
+
+ if (iv_index && (iv_index & 0x01) != iv_flag)
+ iv_index--;
+
+ /* Tester--Drop 90% of packets */
+ /* l_getrandom(&iv_flag, 1); */
+ /* if (iv_flag%10<9) drop = true; */
+
+ if (!drop)
+ print_packet("RX: Network [enc] :", data, size);
+
+ keys = net_packet_decode(net, iv_index, nid, &net_frnd, false,
+ packet + 2, size);
+ if (keys == NULL) {
+ l_debug("Failed to decode packet");
+ /* Remove fast-cache-hash */
+ l_queue_remove(net->fast_cache, isNew);
+ l_free(isNew);
+ return;
+ }
+
+ if (!drop)
+ print_packet("RX: Network [clr] :", packet + 2, size);
+
+ if (!mesh_crypto_packet_parse(packet + 2, size,
+ &net_ctl, &net_ttl,
+ &net_seq,
+ &net_src, &net_dst,
+ &cache_cookie,
+ &net_opcode,
+ &net_segmented,
+ &net_key_id,
+ &net_szmic, &net_relay, &net_seqZero,
+ &net_segO, &net_segN,
+ &msg, &app_msg_len)) {
+ l_error("Failed to parse packet content");
+ return;
+ }
+
+ /* Ignore incoming packets if we are LPN and frnd bit not set */
+ if (net->friend_addr) {
+ struct mesh_subnet *subnet;
+
+ subnet = l_queue_find(net->subnets, match_key_set, keys);
+ if (subnet)
+ return;
+
+ /* If the queue is empty, stop polling */
+ if (net_ctl && net_opcode == NET_OP_FRND_UPDATE && !msg[5])
+ frnd_poll_cancel(net);
+ else
+ frnd_poll(net, false);
+
+ } else if (net_dst == 0) {
+ l_error("illegal parms: DST: %4.4x Ctl: %d TTL: %2.2x",
+ net_dst, net_ctl, net_ttl);
+ return;
+ }
+
+ /* Ignore if we originally sent this */
+ if (is_us(net, net_src, true))
+ return;
+
+ if (drop) {
+ l_info("Dropping SEQ 0x%06x", net_seq);
+ return;
+ }
+
+ l_debug("check %08x", cache_cookie);
+
+ /* As a Relay, suppress repeats of last N packets that pass through */
+ /* The "cache_cookie" should be unique part of App message */
+ if (msg_in_cache(net, net_src, net_seq, cache_cookie))
+ return;
+
+ l_debug("RX: Network %04x -> %04x : TTL 0x%02x : IV : %8.8x SEQ 0x%06x",
+ net_src, net_dst, net_ttl, iv_index, net_seq);
+
+ if (is_us(net, net_dst, false) ||
+ is_lpn_friend(net, net_src, !!(net_frnd)) ||
+ (net_ctl && net_opcode == NET_OP_HEARTBEAT)) {
+
+ l_info("RX: App 0x%04x -> 0x%04x : TTL 0x%02x : SEQ 0x%06x",
+ net_src, net_dst, net_ttl, net_seq);
+
+ l_debug("seq:%x seq0:%x", net_seq, net_seqZero);
+ if (net_ctl) {
+ l_debug("CTL - %4.4x RX", net_seqZero);
+ if (net_opcode == NET_OP_SEG_ACKNOWLEDGE) {
+ /* Illegal to send ACK to non-Unicast Addr */
+ if (net_dst & 0x8000)
+ return;
+
+ /* print_packet("Got ACK", msg, app_msg_len); */
+ /* Pedantic check for correct size */
+ if (app_msg_len != 7)
+ return;
+
+ /* If this is an ACK to our friend queue-only */
+ if (is_lpn_friend(net, net_dst, true))
+ friend_ack_rxed(net, iv_index, net_seq,
+ net_src, net_dst,
+ msg);
+ else
+ ack_received(net, false,
+ net_src, net_dst,
+ net_seqZero,
+ l_get_be32(msg + 3));
+ } else {
+ ctl_received(net, !!(net_frnd), iv_index,
+ net_ttl, net_seq, net_src,
+ net_dst, net_opcode, rssi,
+ msg, app_msg_len);
+ }
+ } else if (net_segmented) {
+ /* If we accept SAR packets to non-Unicast, then
+ * Friend Sar at least needs to be Unicast Only
+ */
+ if (is_lpn_friend(net, net_dst, true) &&
+ !(net_dst & 0x8000)) {
+ /* Check TTL >= 2 before accepting segments
+ * for Friends
+ */
+ if (net_ttl >= 2) {
+ friend_seg_rxed(net, iv_index,
+ net_ttl, net_seq,
+ net_src, net_dst,
+ l_get_be32(packet + 2 + 9),
+ msg, app_msg_len);
+ }
+ } else {
+ seg_rxed(net, net_frnd,
+ iv_index,
+ net_ttl,
+ net_seq,
+ net_src, net_dst,
+ net_key_id,
+ net_szmic, net_seqZero,
+ net_segO, net_segN,
+ msg, app_msg_len);
+ }
+
+ } else {
+ msg_rxed(net, net_frnd,
+ iv_index,
+ net_ttl,
+ net_seq,
+ net_src, net_dst,
+ net_key_id,
+ false, net_seq & SEQ_ZERO_MASK,
+ msg, app_msg_len);
+ }
+
+ if (!!(net_frnd))
+ l_info("Ask for more data!");
+
+ /* If this is one of our Unicast addresses, don't relay */
+ if (net_dst <= 0x7fff)
+ return;
+ }
+
+ if (!net->relay.enable || net_ttl < 0x02 || net_frnd)
+ return;
+
+ packet[2 + 1] = (packet[2 + 1] & ~TTL_MASK) | (net_ttl - 1);
+
+ if (!mesh_crypto_packet_encode(packet + 2, size, keys->enc_key,
+ iv_index, keys->privacy_key)) {
+ l_error("Failed to encode relay packet");
+ return;
+ }
+
+ if (net->relay.enable)
+ send_relay_pkt(net, packet, size + 1);
+}
+
+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;
+
+ if (len <= 2 || !net)
+ return;
+
+ if (info) {
+ net->instant = info->instant;
+ net->chan = info->chan;
+ rssi = info->rssi;
+ }
+
+ packet_received(user_data, data + 1, len - 1, rssi);
+}
+
+static void set_network_beacon(void *a, void *b)
+{
+ struct mesh_subnet *subnet = a;
+ struct mesh_net *net = b;
+ uint8_t beacon_data[22];
+
+ if (!create_secure_beacon(net, subnet, beacon_data,
+ sizeof(beacon_data)))
+ return;
+
+ if (memcmp(&subnet->snb.beacon[1], beacon_data,
+ sizeof(beacon_data)) == 0)
+ return;
+
+ memcpy(&subnet->snb.beacon[1], beacon_data, sizeof(beacon_data));
+
+ if (net->beacon_enable && !net->friend_addr) {
+ print_packet("Set My Beacon to",
+ beacon_data, sizeof(beacon_data));
+ start_network_beacon(subnet, net);
+ }
+
+ if (l_queue_length(net->friends)) {
+ struct mesh_friend_msg update = {
+ .src = net->src_addr,
+ .iv_index = mesh_net_get_iv_index(net),
+ .last_len = 7,
+ .ctl = true,
+ };
+
+ update.u.one[0].hdr = NET_OP_FRND_UPDATE << OPCODE_HDR_SHIFT;
+ update.u.one[0].seq = mesh_net_next_seq_num(net);
+ update.u.one[0].data[0] = NET_OP_FRND_UPDATE;
+ update.u.one[0].data[1] = beacon_data[3];
+ l_put_be32(net->iv_index, update.u.one[0].data + 2);
+ update.u.one[0].data[6] = 0x01; /* More Data */
+ /* print_packet("Frnd-Beacon-SRC",
+ * beacon_data, sizeof(beacon_data));
+ */
+ /* print_packet("Frnd-Update", update.u.one[0].data, 6); */
+
+ l_queue_foreach(net->friends, enqueue_update, &update);
+ }
+}
+
+static void iv_upd_to(struct l_timeout *upd_timeout, void *user_data)
+{
+ struct mesh_net *net = user_data;
+
+ switch (net->iv_upd_state) {
+ case IV_UPD_UPDATING:
+ if (l_queue_length(net->sar_out)) {
+ l_info("don't leave IV Update until sar_out empty");
+ l_timeout_modify(net->iv_update_timeout, 10);
+ break;
+ }
+
+ l_info("iv_upd_state = IV_UPD_NORMAL_HOLD");
+ net->iv_upd_state = IV_UPD_NORMAL_HOLD;
+ l_timeout_modify(net->iv_update_timeout, IV_IDX_UPD_MIN);
+ mesh_net_set_seq_num(net, 0);
+ l_queue_foreach(net->subnets, set_network_beacon, net);
+ mesh_net_flush_msg_queues(net);
+ break;
+
+ case IV_UPD_INIT:
+ case IV_UPD_NORMAL_HOLD:
+ case IV_UPD_NORMAL:
+ l_timeout_remove(upd_timeout);
+ net->iv_update_timeout = NULL;
+ l_info("iv_upd_state = IV_UPD_NORMAL");
+ net->iv_upd_state = IV_UPD_NORMAL;
+ if (net->seq_num > IV_UPDATE_SEQ_TRIGGER)
+ mesh_net_iv_index_update(net);
+ break;
+ }
+}
+
+static void update_iv_kr_state(struct mesh_subnet *subnet, uint32_t iv_index,
+ bool iv_update, bool kr_transition,
+ bool rxed_key_refresh, bool lpn)
+{
+ struct mesh_net *net = subnet->net;
+ uint8_t local_kr;
+ uint32_t local_iv_index;
+ bool local_iv_update;
+
+ /* Save original settings to avoid resetting same values,
+ * and secure beacon timer
+ */
+ local_iv_index = net->iv_index;
+ local_kr = subnet->key_refresh;
+ local_iv_update = iv_is_updating(net);
+
+ if (iv_index != local_iv_index || kr_transition)
+ l_info("SNB-RX: %8.8x - Key Refresh: %d IV Update: %d",
+ iv_index, rxed_key_refresh, iv_update);
+
+ if (iv_update && (net->iv_upd_state > IV_UPD_UPDATING)) {
+ if (iv_index != net->iv_index)
+ l_error("Update attempted to0 soon (Normal < MIN)");
+
+ return;
+ }
+
+ if (net->iv_upd_state == IV_UPD_INIT) {
+ if (iv_index > net->iv_index)
+ mesh_net_set_seq_num(net, 0);
+ net->iv_index = iv_index;
+
+ if (iv_update) {
+ /* Other devices will be accepting old or new iv_index,
+ * but we don't know how far through update they are.
+ * Starting permissive state will allow us maximum
+ * (96 hours) to resync
+ */
+ l_info("iv_upd_state = IV_UPD_UPDATING");
+ net->iv_upd_state = IV_UPD_UPDATING;
+ net->iv_update_timeout = l_timeout_create(
+ IV_IDX_UPD_MIN, iv_upd_to, net, NULL);
+ } else {
+ l_info("iv_upd_state = IV_UPD_NORMAL");
+ net->iv_upd_state = IV_UPD_NORMAL;
+ }
+
+ storage_local_set_iv_index(net, iv_index, net->iv_upd_state);
+
+ /* Figure out the key refresh phase */
+ if (kr_transition) {
+ if (rxed_key_refresh)
+ mesh_net_key_refresh_phase_two(net,
+ subnet->idx);
+ else
+ mesh_net_key_refresh_finish(net, subnet->idx);
+ }
+
+ if (!lpn)
+ set_network_beacon(subnet, net);
+
+ return;
+ }
+
+ if (iv_update && !iv_is_updating(net)) {
+ l_info("iv_upd_state = IV_UPD_UPDATING");
+ 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);
+ } else if (iv_update && iv_index != net->iv_index) {
+ l_error("Update attempted too soon (iv idx already updated)");
+ return;
+ }
+
+ if (iv_index != local_iv_index || kr_transition)
+ l_info("IVindex 0x%8.8x / Key Refresh update received",
+ 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);
+ }
+
+ /* Figure out the key refresh phase */
+ if (kr_transition) {
+ if (rxed_key_refresh)
+ mesh_net_key_refresh_phase_two(net, subnet->idx);
+ else
+ mesh_net_key_refresh_finish(net, subnet->idx);
+ }
+
+ if (!lpn)
+ return;
+
+ if (local_kr != subnet->key_refresh ||
+ local_iv_index != net->iv_index ||
+ local_iv_update != iv_is_updating(net))
+ set_network_beacon(subnet, net);
+}
+
+static void process_beacon(void *user_data, const void *data,
+ uint8_t size, int8_t rssi)
+{
+ struct mesh_net *net = user_data;
+ const uint8_t *buf = data;
+ uint32_t iv_index;
+ uint64_t cmac;
+ bool iv_update, rxed_iv_update, rxed_key_refresh;
+ struct mesh_subnet *subnet;
+ struct net_key *keys;
+ bool kr_transition = false;
+
+ if (size != 22 || buf[0] != 0x01)
+ return;
+
+ /* print_packet("Secure Net Beacon RXed", data, size); */
+ rxed_key_refresh = (buf[1] & 0x01) == 0x01;
+ rxed_iv_update = iv_update = (buf[1] & 0x02) == 0x02;
+ iv_index = l_get_be32(buf + 10);
+
+ l_debug("KR: %d -- IVU: %d -- IV: %8.8x",
+ rxed_key_refresh, rxed_iv_update, iv_index);
+
+ /* Inhibit recognizing iv_update true-->false
+ * if we have outbound SAR messages in flight
+ */
+ if (l_queue_length(net->sar_out)) {
+ if (!iv_update && iv_update != iv_is_updating(net))
+ iv_update = true;
+ }
+
+ subnet = l_queue_find(net->subnets, match_network_id, buf + 2);
+ if (!subnet)
+ return;
+
+ /* Check if Key Refresh flag value is different from
+ * the locally stored one
+ */
+ if (rxed_key_refresh != subnet->key_refresh)
+ kr_transition = true;
+
+ /* If the local node is a provisioner or there are no new keys,
+ * ignore KR beacon setting
+ */
+ if (net->provisioner)
+ kr_transition = false;
+ else if (subnet->updated.key_set.nid == NET_NID_INVALID)
+ kr_transition = false;
+ /* If beacon's key refresh bit is not set and the beacon is encoded
+ * with the "new" network key, this signals transition from
+ * key refresh procedure to normal operation
+ */
+ else if (!rxed_key_refresh &&
+ !memcmp(subnet->updated.network_id, buf + 2, 8))
+ kr_transition = true;
+
+ if ((net->iv_index + IV_IDX_DIFF_RANGE < iv_index) ||
+ (iv_index < net->iv_index)) {
+ l_info("iv index outside range");
+ return;
+ }
+
+ /* Don't bother going further if nothing has changed */
+ if (!memcmp(&subnet->snb.beacon[1], buf, size)) {
+ subnet->snb.observed++;
+ return;
+ }
+
+ if (!rxed_key_refresh && !subnet->key_refresh && !kr_transition)
+ keys = &subnet->current;
+ else if (subnet->updated.key_set.nid != NET_NID_INVALID)
+ keys = &subnet->updated;
+ else
+ return;
+
+ if (memcmp(keys->network_id, buf + 2, 8))
+ return;
+
+ /* Any behavioral changes must pass CMAC test */
+ if (!mesh_crypto_beacon_cmac(keys->beacon_key, keys->network_id,
+ iv_index, rxed_key_refresh,
+ rxed_iv_update, &cmac)) {
+ l_error("mesh_crypto_beacon_cmac failed");
+ return;
+ }
+
+ if (cmac != l_get_be64(buf + 14)) {
+ l_error("cmac compare failed %16.16lx != %16.16lx",
+ cmac, l_get_be64(buf + 14));
+ return;
+ }
+
+ if (iv_index == net->iv_index &&
+ iv_is_updating(net) == iv_update && !kr_transition) {
+ l_info("No change: IV index %4.4x, rxed KR = %d ",
+ iv_index, rxed_key_refresh);
+ if (net->iv_upd_state == IV_UPD_INIT) {
+ l_info("iv_upd_state = IV_UPD_NORMAL");
+ net->iv_upd_state = IV_UPD_NORMAL;
+ }
+
+ subnet->snb.observed++;
+ return;
+ }
+
+ subnet->snb.observed++;
+
+ update_iv_kr_state(subnet, iv_index, iv_update, kr_transition,
+ rxed_key_refresh, false);
+}
+
+static void lpn_process_beacon(void *user_data, const void *data,
+ uint8_t size, int8_t rssi)
+{
+ struct mesh_net *net = user_data;
+ const uint8_t *buf = data;
+ uint32_t iv_index;
+ bool iv_update, rxed_key_refresh;
+ struct mesh_subnet *subnet;
+ bool kr_transition = false;
+
+ /* print_packet("lpn: Secure Net Beacon RXed", data, size); */
+ rxed_key_refresh = (buf[0] & 0x01) == 0x01;
+ iv_index = l_get_be32(buf + 1);
+
+ l_debug("KR: %d -- IVU: %d -- IV: %8.8x",
+ rxed_key_refresh, iv_index);
+
+ /* Inhibit recognizing iv_update true-->false if we have outbound
+ * SAR messages in flight
+ */
+ if (l_queue_length(net->sar_out)) {
+ if (!iv_update && iv_update != iv_is_updating(net))
+ iv_update = true;
+ }
+
+ /* TODO: figure out actual network index (i.e., friendship subnet) */
+ subnet = get_primary_subnet(net);
+ if (!subnet)
+ return;
+
+ /* Check if Key Refresh flag value is different from
+ * the locally stored one
+ */
+ if (rxed_key_refresh != subnet->key_refresh)
+ kr_transition = true;
+
+ /* If the local node is a provisioner or there are no new keys,
+ * ignore KR beacon setting
+ */
+ if (subnet->updated.key_set.nid == NET_NID_INVALID)
+ kr_transition = false;
+
+ if ((net->iv_index + IV_IDX_DIFF_RANGE < iv_index) ||
+ (iv_index < net->iv_index)) {
+ l_info("iv index outside range");
+ return;
+ }
+
+ /* Don't bother going further if nothing has changed */
+ if (!kr_transition && iv_index == net->iv_index &&
+ iv_update == iv_is_updating(net) &&
+ net->iv_upd_state != IV_UPD_INIT)
+ return;
+
+ if (iv_index == net->iv_index &&
+ iv_is_updating(net) == iv_update && !kr_transition) {
+ l_info("No change: IV index %4.4x, rxed KR = %d ",
+ iv_index, rxed_key_refresh);
+ if (net->iv_upd_state == IV_UPD_INIT) {
+ l_info("iv_upd_state = IV_UPD_NORMAL");
+ net->iv_upd_state = IV_UPD_NORMAL;
+ }
+ return;
+ }
+
+ update_iv_kr_state(subnet, iv_index, iv_update, kr_transition,
+ rxed_key_refresh, true);
+}
+
+static void beacon_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;
+
+ if (len <= 2 || !net)
+ return;
+
+ if (info) {
+ net->instant = info->instant;
+ net->chan = info->chan;
+ rssi = info->rssi;
+ }
+
+ process_beacon(user_data, data + 1, len - 1, rssi);
+}
+
+bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable)
+{
+ if (!net || !IS_UNASSIGNED(net->friend_addr))
+ return false;
+
+ if (net->beacon_enable != enable) {
+ uint8_t type = MESH_AD_TYPE_BEACON;
+
+ net->beacon_enable = enable;
+
+ if (!enable)
+ mesh_io_send_cancel(net->io, &type, 1);
+
+ l_queue_foreach(net->subnets, start_network_beacon, net);
+ }
+
+ return true;
+}
+
+
+bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)
+{
+ if (!net)
+ return false;
+
+ net->io = io;
+
+ return true;
+}
+
+struct mesh_io *mesh_net_detach(struct mesh_net *net)
+{
+ struct mesh_io *io;
+ uint8_t type = 0;
+
+ if (!net)
+ return NULL;
+
+ io = net->io;
+
+ mesh_io_send_cancel(net->io, &type, 1);
+ mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
+ mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_NET);
+
+ net->io = NULL;
+
+ return io;
+}
+
+bool mesh_net_iv_index_update(struct mesh_net *net)
+{
+ if (net->iv_upd_state != IV_UPD_NORMAL)
+ return false;
+
+ l_info("iv_upd_state = IV_UPD_UPDATING");
+ 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))
+ return false;
+
+ l_queue_foreach(net->subnets, set_network_beacon, net);
+ net->iv_update_timeout = l_timeout_create(
+ IV_IDX_UPD_MIN,
+ iv_upd_to, net, NULL);
+
+ return true;
+}
+
+
+
+void mesh_net_sub_list_add(struct mesh_net *net, uint16_t addr)
+{
+ uint8_t msg[11] = { PROXY_OP_FILTER_ADD };
+ uint8_t n = 1;
+
+ l_put_be16(addr, msg + n);
+ n += 2;
+
+ mesh_net_transport_send(net, NULL, false,
+ mesh_net_get_iv_index(net), 0,
+ 0, 0, 0, msg, n);
+}
+
+void mesh_net_sub_list_del(struct mesh_net *net, uint16_t addr)
+{
+ uint8_t msg[11] = { PROXY_OP_FILTER_DEL };
+ uint8_t n = 1;
+
+ l_put_be16(addr, msg + n);
+ n += 2;
+
+ mesh_net_transport_send(net, NULL, false,
+ mesh_net_get_iv_index(net), 0,
+ 0, 0, 0, msg, n);
+}
+
+bool mesh_net_dst_reg(struct mesh_net *net, uint16_t dst)
+{
+ struct mesh_destination *dest = l_queue_find(net->destinations,
+ match_by_dst, L_UINT_TO_PTR(dst));
+
+ if (IS_UNASSIGNED(dst) || IS_ALL_NODES(dst))
+ return false;
+
+ if (!dest) {
+ dest = l_new(struct mesh_destination, 1);
+
+ if (dst < 0x8000)
+ l_queue_push_head(net->destinations, dest);
+ else
+ l_queue_push_tail(net->destinations, dest);
+
+ /* If LPN, and Group/Virtual, add to Subscription List */
+ if (net->friend_addr) {
+ /* TODO: Fix this garbage */
+ uint32_t u32_dst[7] = {dst, 0xffffffff};
+
+ frnd_sub_add(net, u32_dst);
+ }
+ }
+
+ dest->dst = dst;
+ dest->ref_cnt++;
+
+ return true;
+}
+
+bool mesh_net_dst_unreg(struct mesh_net *net, uint16_t dst)
+{
+ struct mesh_destination *dest = l_queue_find(net->destinations,
+ match_by_dst, L_UINT_TO_PTR(dst));
+
+ if (!dest)
+ return false;
+
+ if (dest->ref_cnt)
+ dest->ref_cnt--;
+
+ if (dest->ref_cnt)
+ return true;
+
+ /* TODO: If LPN, and Group/Virtual, remove from Subscription List */
+ if (net->friend_addr) {
+ /* TODO: Fix this garbage */
+ uint32_t u32_dst[7] = {dst, 0xffffffff};
+
+ frnd_sub_del(net, u32_dst);
+ }
+
+ l_queue_remove(net->destinations, dest);
+
+ l_free(dest);
+ return true;
+}
+
+bool mesh_net_flush(struct mesh_net *net)
+{
+ if (!net)
+ return false;
+
+ /* TODO mesh-io Flush */
+ return true;
+}
+
+/* TODO: add net key index */
+static bool send_seg(struct mesh_net *net, struct mesh_sar *msg, uint8_t segO)
+{
+ uint8_t seg_len;
+ uint8_t gatt_data[30];
+ uint8_t *packet = gatt_data;
+ uint8_t packet_len;
+ uint8_t segN = SEG_MAX(msg->len);
+ uint16_t seg_off = SEG_OFF(segO);
+ struct mesh_key_set *key_set = NULL;
+ uint32_t seq_num = mesh_net_next_seq_num(net);
+
+ if (segN) {
+ if (msg->len - seg_off > SEG_OFF(1))
+ seg_len = SEG_OFF(1);
+ else
+ seg_len = msg->len - seg_off;
+ } else {
+ seg_len = msg->len;
+ }
+
+ /* Start IV Update procedure when we hit our trigger point */
+ if (!msg->frnd && net->seq_num > IV_UPDATE_SEQ_TRIGGER)
+ mesh_net_iv_index_update(net);
+
+ l_debug("segN %d segment %d seg_off %d", segN, segO, seg_off);
+ /* print_packet("Sending", msg->buf + seg_off, seg_len); */
+ {
+ /* TODO: Are we RXing on an LPN's behalf? Then set RLY bit */
+
+ if (!mesh_crypto_packet_build(false, msg->ttl,
+ seq_num,
+ msg->src, msg->remote,
+ 0,
+ segN ? true : false, msg->key_id,
+ msg->szmic, false, msg->seqZero,
+ segO, segN,
+ msg->buf + seg_off, seg_len,
+ packet + 1, &packet_len)) {
+ l_error("Failed to build packet");
+ return false;
+ }
+ }
+ print_packet("Clr-Net Tx", packet + 1, packet_len);
+
+ if (msg->frnd_cred && net->friend_addr)
+ key_set = frnd_get_key(net);
+
+ if (key_set == NULL) {
+ struct mesh_subnet *subnet = get_primary_subnet(net);
+
+ key_set = &subnet->tx->key_set;
+ }
+
+ if (!mesh_crypto_packet_encode(packet + 1, packet_len, key_set->enc_key,
+ msg->iv_index, key_set->privacy_key)) {
+ l_error("Failed to encode packet");
+ return false;
+ }
+
+ print_packet("Step 3", packet + 1, packet_len);
+ if (!mesh_crypto_packet_label(packet + 1, packet_len,
+ msg->iv_index, key_set->nid)) {
+ l_error("Failed to label packet");
+ return false;
+ }
+ /* print_packet("Step 4", packet + 1, packet_len); */
+
+ {
+ char *str;
+
+ send_msg_pkt(net, packet, packet_len + 1);
+
+ str = l_util_hexstring(packet + 1, packet_len);
+ l_info("TX: Network %04x -> %04x : %s (%u) : TTL %d : SEQ %06x",
+ msg->src, msg->remote, str,
+ packet_len, msg->ttl,
+ msg->frnd ? msg->seqAuth + segO : seq_num);
+ l_free(str);
+ }
+
+ msg->last_seg = segO;
+
+ return true;
+}
+
+void mesh_net_send_seg(struct mesh_net *net, struct mesh_key_set *key_set,
+ uint32_t iv_index,
+ uint8_t ttl,
+ uint32_t seq,
+ uint16_t src, uint16_t dst,
+ uint32_t hdr,
+ const void *seg, uint16_t seg_len)
+{
+ char *str;
+ uint8_t packet[30];
+ uint8_t packet_len;
+ bool segmented = !!((hdr >> SEG_HDR_SHIFT) & true);
+ uint8_t key_id = (hdr >> KEY_HDR_SHIFT) & KEY_ID_MASK;
+ bool szmic = !!((hdr >> SZMIC_HDR_SHIFT) & true);
+ uint16_t seqZero = (hdr >> SEQ_ZERO_HDR_SHIFT) & SEQ_ZERO_MASK;
+ uint8_t segO = (hdr >> SEGO_HDR_SHIFT) & SEG_MASK;
+ uint8_t segN = (hdr >> SEGN_HDR_SHIFT) & SEG_MASK;
+
+ /* TODO: Only used for current POLLed segments to LPNs */
+
+ l_debug("SEQ: %6.6x", seq + segO);
+ l_debug("SEQ0: %6.6x", seq);
+ l_debug("segO: %d", segO);
+
+ if (!mesh_crypto_packet_build(false, ttl,
+ seq,
+ src, dst,
+ 0,
+ segmented, key_id,
+ szmic, false, seqZero,
+ segO, segN,
+ seg, seg_len,
+ packet + 1, &packet_len)) {
+ l_error("Failed to build packet");
+ return;
+ }
+
+ if (!mesh_crypto_packet_encode(packet + 1, packet_len, key_set->enc_key,
+ iv_index, key_set->privacy_key)) {
+ l_error("Failed to encode packet");
+ return;
+ }
+
+ /* print_packet("Step 3", packet + 0, packet_len); */
+ if (!mesh_crypto_packet_label(packet + 1, packet_len, iv_index,
+ key_set->nid)) {
+ l_error("Failed to label packet");
+ return;
+ }
+ /* print_packet("Step 4", packet + 1, packet_len); */
+
+ send_msg_pkt(net, packet, packet_len + 1);
+
+ str = l_util_hexstring(packet + 1, packet_len);
+ l_info("TX: Friend Seg-%d %04x -> %04x : %s (%u) : TTL %d : SEQ %06x",
+ segO, src, dst, str, packet_len, ttl, seq);
+ 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,
+ 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;
+
+ if (!src)
+ src = net->src_addr;
+
+ if (!src || !dst)
+ return 0;
+
+ if (ttl == 0xff)
+ ttl = net->default_ttl;
+
+ seg_max = SEG_MAX(msg_len);
+
+ /* First enqueue to any Friends and internal models */
+ result = msg_rxed(net, false,
+ iv_index,
+ ttl,
+ seq + seg_max,
+ src, dst,
+ key_id,
+ szmic, seq & SEQ_ZERO_MASK,
+ msg, msg_len);
+
+ /* If successfully enqued or delivered
+ * to Unicast address, we are done
+ */
+ if (result || src == dst ||
+ (dst >= net->src_addr && dst <= net->last_addr)) {
+ /* Adjust our seq_num for "virtual" delivery */
+ net->seq_num += seg_max;
+ mesh_net_next_seq_num(net);
+ return 0;
+ }
+
+ /* If Segmented, Cancel any OB segmented message to same DST */
+ if (seg_max) {
+ payload = l_queue_remove_if(net->sar_out, match_sar_remote,
+ L_UINT_TO_PTR(dst));
+ mesh_sar_free(payload);
+ }
+
+ /* Setup OTA Network send */
+ payload = mesh_sar_new(msg_len);
+ memcpy(payload->buf, msg, msg_len);
+ payload->len = msg_len;
+ payload->src = src;
+ payload->remote = dst;
+ payload->ttl = ttl;
+ payload->szmic = szmic;
+ payload->frnd_cred = frnd_cred;
+ payload->key_id = key_id;
+ if (seg_max) {
+ payload->flags = 0xffffffff >> (31 - seg_max);
+ payload->seqZero = seq & SEQ_ZERO_MASK;
+ }
+
+ payload->iv_index = mesh_net_get_iv_index(net);
+ payload->seqAuth = net->seq_num;
+
+ result = true;
+ if (!IS_UNICAST(dst) && seg_max) {
+ for (int i = 0; i < 4; i++) {
+ for (seg = 0; seg <= seg_max && result; seg++)
+ result = send_seg(net, payload, seg);
+ }
+ } else {
+ for (seg = 0; seg <= seg_max && result; seg++)
+ result = send_seg(net, payload, seg);
+ }
+
+ /* Reliable: Cache; Unreliable: Flush*/
+ if (result && seg_max && IS_UNICAST(dst)) {
+ l_queue_push_head(net->sar_out, payload);
+ payload->seg_timeout =
+ l_timeout_create(SEG_TO, outseg_to, net, NULL);
+ payload->msg_timeout =
+ 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;
+ } 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);
+}
+
+/* TODO: add net key index */
+void mesh_net_ack_send(struct mesh_net *net, struct mesh_key_set *key_set,
+ uint32_t iv_index,
+ uint8_t ttl,
+ uint32_t seq,
+ uint16_t src, uint16_t dst,
+ bool rly, uint16_t seqZero,
+ uint32_t ack_flags)
+{
+ uint32_t hdr;
+ uint8_t data[7];
+ uint8_t pkt_len;
+ uint8_t pkt[30];
+ char *str;
+
+ hdr = NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT;
+ hdr |= rly << RELAY_HDR_SHIFT;
+ hdr |= (seqZero & SEQ_ZERO_MASK) << SEQ_ZERO_HDR_SHIFT;
+ l_put_be32(hdr, data);
+ l_put_be32(ack_flags, data + 3);
+ if (!mesh_crypto_packet_build(true, ttl,
+ seq,
+ src, dst,
+ NET_OP_SEG_ACKNOWLEDGE,
+ false, /* Not Segmented */
+ 0, /* No Key ID associated */
+ false, rly, seqZero,
+ 0, 0, /* no segO or segN */
+ data + 1, 6,
+ pkt + 1, &pkt_len)) {
+ return;
+ }
+
+ if (key_set == NULL) {
+ struct mesh_subnet *subnet = get_primary_subnet(net);
+
+ key_set = &subnet->tx->key_set;
+ }
+
+ if (!mesh_crypto_packet_encode(pkt + 1, pkt_len, key_set->enc_key,
+ iv_index, key_set->privacy_key)) {
+ l_error("Failed to encode packet");
+ return;
+ }
+
+ /* print_packet("Step 3", pkt, pkt_len); */
+ if (!mesh_crypto_packet_label(pkt + 1, pkt_len,
+ iv_index, key_set->nid)) {
+ l_error("Failed to label packet");
+ return;
+ }
+
+ /* print_packet("Step 4", pkt, pkt_len); */
+ send_msg_pkt(net, pkt, pkt_len + 1);
+
+ str = l_util_hexstring(pkt + 1, pkt_len);
+ l_info("TX: Friend ACK %04x -> %04x : %s (%u) : TTL %d : SEQ %06x",
+ src, dst, str, pkt_len,
+ ttl, seq);
+ l_free(str);
+}
+
+/* TODO: add net key index */
+void mesh_net_transport_send(struct mesh_net *net, struct mesh_key_set *key_set,
+ bool fast, uint32_t iv_index, uint8_t ttl,
+ uint32_t seq, uint16_t src, uint16_t dst,
+ const uint8_t *msg, uint16_t msg_len)
+{
+ uint32_t use_seq = seq;
+ uint8_t pkt_len;
+ uint8_t pkt[30];
+ bool result = false;
+
+ if (!net->src_addr)
+ return;
+
+ if (!src)
+ src = net->src_addr;
+
+ if (src == dst)
+ return;
+
+ if (ttl == 0xff)
+ ttl = net->default_ttl;
+
+ /* Range check the Opcode and msg length*/
+ if (*msg & 0xc0 || (9 + msg_len + 8 > 29))
+ return;
+
+ /* Enqueue for Friend if forwardable and from us */
+ if (!(key_set) && src >= net->src_addr && src <= net->last_addr) {
+ uint32_t hdr = msg[0] << OPCODE_HDR_SHIFT;
+ uint8_t frnd_ttl = ttl;
+
+ if (friend_packet_queue(net, iv_index,
+ true, frnd_ttl,
+ mesh_net_next_seq_num(net),
+ src, dst,
+ hdr,
+ msg + 1, msg_len - 1)) {
+ return;
+ }
+ }
+
+ /* Deliver to Local entities if applicable */
+ if (!(dst & 0x8000) && src >= net->src_addr && src <= net->last_addr) {
+ result = ctl_received(net, !!(key_set),
+ iv_index, ttl,
+ mesh_net_next_seq_num(net),
+ src, dst,
+ msg[0], 0, msg + 1, msg_len - 1);
+ }
+
+ if (key_set == NULL) {
+ struct mesh_subnet *subnet = get_primary_subnet(net);
+
+ key_set = &subnet->tx->key_set;
+ use_seq = mesh_net_next_seq_num(net);
+
+ if (result || (dst >= net->src_addr && dst <= net->last_addr))
+ return;
+ }
+
+ if (!mesh_crypto_packet_build(true, ttl,
+ use_seq,
+ src, dst,
+ msg[0],
+ false, 0,
+ false, false, 0,
+ 0, 0,
+ msg + 1, msg_len - 1,
+ pkt + 1, &pkt_len))
+ return;
+
+ /* print_packet("Step 2", pkt + 1, pkt_len); */
+
+ if (!mesh_crypto_packet_encode(pkt + 1, pkt_len, key_set->enc_key,
+ iv_index, key_set->privacy_key)) {
+ l_error("Failed to encode pkt");
+ return;
+ }
+
+ /* print_packet("Step 3", pkt + 1, pkt_len); */
+ if (!mesh_crypto_packet_label(pkt, pkt_len, iv_index,
+ key_set->nid)) {
+ l_error("Failed to label pkt");
+ return;
+ }
+
+ /* print_packet("Step 4", pkt + 1, pkt_len); */
+
+ if (dst != 0) {
+ char *str;
+
+ send_msg_pkt(net, pkt, pkt_len + 1);
+
+ str = l_util_hexstring(pkt + 1, pkt_len);
+ l_info("TX: Network %04x -> %04x : %s (%u) : TTL %d : SEQ %06x",
+ src, dst, str, pkt_len,
+ ttl, use_seq);
+ l_free(str);
+ }
+}
+
+uint8_t mesh_net_key_refresh_phase_set(struct mesh_net *net, uint16_t idx,
+ uint8_t transition)
+{
+ struct mesh_subnet *subnet;
+
+ if (!net)
+ return MESH_STATUS_UNSPECIFIED_ERROR;
+
+ subnet = l_queue_find(net->subnets, match_key_index,
+ L_UINT_TO_PTR(idx));
+ if (!subnet)
+ return MESH_STATUS_INVALID_NETKEY;
+
+ if (transition == subnet->kr_phase)
+ return MESH_STATUS_SUCCESS;
+
+ if ((transition != 2 && transition != 3) ||
+ transition < subnet->kr_phase)
+ return MESH_STATUS_CANNOT_SET;
+
+ switch (transition) {
+ case 2:
+ if (mesh_net_key_refresh_phase_two(net, idx)
+ != MESH_STATUS_SUCCESS)
+ return MESH_STATUS_CANNOT_SET;
+ break;
+ case 3:
+ if (mesh_net_key_refresh_finish(net, idx)
+ != MESH_STATUS_SUCCESS)
+ return MESH_STATUS_CANNOT_SET;
+ break;
+ default:
+ return MESH_STATUS_CANNOT_SET;
+ }
+ return MESH_STATUS_SUCCESS;
+}
+
+uint8_t mesh_net_key_refresh_phase_get(struct mesh_net *net, uint16_t idx,
+ uint8_t *phase)
+{
+ struct mesh_subnet *subnet;
+
+ if (!net)
+ return MESH_STATUS_UNSPECIFIED_ERROR;
+
+ subnet = l_queue_find(net->subnets, match_key_index,
+ L_UINT_TO_PTR(idx));
+ if (!subnet)
+ return MESH_STATUS_INVALID_NETKEY;
+
+ *phase = subnet->kr_phase;
+ return MESH_STATUS_SUCCESS;
+}
+
+int mesh_net_kr_phase_one(struct mesh_net *net, uint16_t idx,
+ const uint8_t *value)
+{
+ struct mesh_subnet *subnet;
+
+ if (!net)
+ return MESH_STATUS_UNSPECIFIED_ERROR;
+
+ subnet = l_queue_find(net->subnets, match_key_index,
+ L_UINT_TO_PTR(idx));
+ if (!subnet)
+ return MESH_STATUS_CANNOT_UPDATE;
+
+ if (subnet->updated.key_set.nid != NET_NID_INVALID)
+ l_info("Warning: overwriting new keys");
+
+ /* Preserve starting data */
+ subnet->updated = subnet->current;
+
+ /* Generate new keys */
+ if (!create_keys(net, &subnet->updated, value)) {
+ subnet->updated.key_set.nid = NET_NID_INVALID;
+ l_error("Failed to start key refresh phase one");
+ return MESH_STATUS_CANNOT_UPDATE;
+ }
+
+ /* If we are an LPN, generate our keys here */
+ if (net->friend_addr)
+ frnd_key_refresh(net, 1);
+ else
+ /* If we are a Friend-Node, generate all our new keys */
+ l_queue_foreach(net->friends, frnd_kr_phase1, (void *)value);
+
+ l_info("key refresh phase 1: NID 0x%2x", subnet->updated.key_set.nid);
+
+ mesh_net_add_keyset(net, &subnet->updated.key_set);
+ subnet->kr_phase = KEY_REFRESH_PHASE_ONE;
+
+ return MESH_STATUS_SUCCESS;
+}
+
+int mesh_net_key_refresh_phase_two(struct mesh_net *net, uint16_t idx)
+{
+ struct mesh_subnet *subnet;
+
+ if (!net)
+ return MESH_STATUS_UNSPECIFIED_ERROR;
+
+ subnet = l_queue_find(net->subnets, match_key_index,
+ L_UINT_TO_PTR(idx));
+
+ if (!subnet || subnet->updated.key_set.nid == NET_NID_INVALID)
+ return MESH_STATUS_INVALID_NETKEY;
+
+ l_info("Key refresh procedure phase 2: start using new net TX keys");
+ subnet->key_refresh = 1;
+ subnet->tx = &subnet->updated;
+ /* TODO: Provisioner may need to stay in phase three until
+ * it hears beacons from all the nodes
+ */
+ subnet->kr_phase = KEY_REFRESH_PHASE_TWO;
+ set_network_beacon(subnet, net);
+
+ if (net->friend_addr)
+ frnd_key_refresh(net, 2);
+ else
+ l_queue_foreach(net->friends, frnd_kr_phase2, net);
+
+ return MESH_STATUS_SUCCESS;
+}
+
+int mesh_net_key_refresh_finish(struct mesh_net *net, uint16_t idx)
+{
+ struct mesh_subnet *subnet;
+
+ if (!net)
+ return MESH_STATUS_UNSPECIFIED_ERROR;
+
+ subnet = l_queue_find(net->subnets, match_key_index,
+ L_UINT_TO_PTR(idx));
+
+ if (!subnet || subnet->updated.key_set.nid == NET_NID_INVALID)
+ return MESH_STATUS_INVALID_NETKEY;
+
+ if (subnet->kr_phase == KEY_REFRESH_PHASE_NONE)
+ return MESH_STATUS_SUCCESS;
+
+ l_info("Key refresh phase 3: use new keys only, discard old ones");
+
+ /* Switch to using new keys, discard old ones */
+ subnet->current = subnet->updated;
+ subnet->tx = &subnet->current;
+ subnet->updated.key_set.nid = NET_NID_INVALID;
+ mesh_net_remove_keyset(net, &subnet->updated.key_set);
+ subnet->key_refresh = 0;
+ subnet->kr_phase = KEY_REFRESH_PHASE_NONE;
+ set_network_beacon(subnet, net);
+
+ if (net->friend_addr)
+ frnd_key_refresh(net, 3);
+ else
+ l_queue_foreach(net->friends, frnd_kr_phase3, net);
+
+ return MESH_STATUS_SUCCESS;
+}
+
+void mesh_net_heartbeat_send(struct mesh_net *net)
+{
+ struct mesh_net_heartbeat *hb = &net->heartbeat;
+ uint8_t msg[4];
+ int n = 0;
+
+ if (hb->pub_dst == UNASSIGNED_ADDRESS)
+ return;
+
+ msg[n++] = NET_OP_HEARTBEAT;
+ msg[n++] = hb->pub_ttl;
+ l_put_be16(hb->features, msg + n);
+ n += 2;
+
+ mesh_net_transport_send(net, NULL, false, mesh_net_get_iv_index(net),
+ hb->pub_ttl, 0, 0, hb->pub_dst, msg, n);
+}
+
+void mesh_net_heartbeat_init(struct mesh_net *net)
+{
+ struct mesh_net_heartbeat *hb = &net->heartbeat;
+
+ memset(hb, 0, sizeof(struct mesh_net_heartbeat));
+ hb->sub_min_hops = 0xff;
+ hb->features = mesh_net_get_features(net);
+}
+
+uint32_t mesh_net_friend_timeout(struct mesh_net *net, uint16_t addr)
+{
+ struct mesh_friend *frnd = l_queue_find(net->friends, match_by_friend,
+ L_UINT_TO_PTR(addr));
+
+ if (!frnd)
+ return 0;
+ else
+ return frnd->poll_timeout;
+}
+
+bool mesh_net_local_node_set(struct mesh_net *net, struct mesh_node *node,
+ bool provisioner)
+{
+ if (net->local_node) {
+ l_info("Local node already registered");
+ return false;
+ }
+
+ net->local_node = node;
+ net->provisioner = provisioner;
+
+ return true;
+}
+
+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)
+{
+ struct mesh_io *io;
+
+ if (!net)
+ return;
+
+ net->provisioned = provisioned;
+ io = net->io;
+
+ if (provisioned) {
+ 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);
+ } else {
+ uint8_t *uuid = node_uuid_get(net->local_node);
+
+ if (!uuid)
+ return;
+
+ mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
+ mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_NET);
+
+ mesh_prov_listen(net, uuid, (uint8_t *) &net->prov_caps,
+ acceptor_prov_open,
+ acceptor_prov_close,
+ acceptor_prov_receive, net);
+ }
+}
--
2.14.3
next prev parent reply other threads:[~2018-04-25 18:20 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-04-25 18:20 [PATCH BlueZ v3 00/14] Bluetooth Mesh Daemon Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 01/14] meshd: Shared private meshd interfaces Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 02/14] meshd: Mesh crypto support Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 03/14] meshd: Infrastructure for Mesh daemon Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 04/14] meshd: Initial Mesh Friendship support Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 05/14] meshd: Provisioning logic for mesh Brian Gix
2018-04-25 18:20 ` Brian Gix [this message]
2018-04-25 18:20 ` [PATCH BlueZ v3 07/14] meshd: Add Accessors to Transport layer data Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 08/14] meshd: Header files for mesh access layer and utilities Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 09/14] meshd: Source " Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 10/14] meshd: Source code for handling access layer mux/demux Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 11/14] meshd: Mesh config server model Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 12/14] meshd: Read and write mesh configuration in JSON format Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 13/14] meshd: Sample device composition in JSON fromat Brian Gix
2018-04-25 18:20 ` [PATCH BlueZ v3 14/14] Makefile for meshd and configure.ac Brian Gix
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180425182042.31938-7-brian.gix@intel.com \
--to=brian.gix@intel.com \
--cc=linux-bluetooth@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).