ble_mesh: Check if subnet exists before updating beacon_sent
Before updating the "beacon_sent" of a subnet, we need to check
if the subnet still exists, especially for a Provisioner.
Fix a bug which will cause Provisioner failed to send Secure
Network Beacon if no device is provisioned
/* 1 transmission, 20ms interval */
#define PROV_XMIT BLE_MESH_TRANSMIT(0, 20)
+#define SNB_NET_IDX_SET(_val) ((void *)((u32_t)(_val)))
+#define SNB_NET_IDX_GET(_ptr) ((u32_t)(_ptr))
+
static struct k_delayed_work beacon_timer;
static struct bt_mesh_subnet *cache_check(u8_t data[21])
@@ -71,12 +74,31 @@ static void cache_add(u8_t data[21], struct bt_mesh_subnet *sub)
static void beacon_complete(int err, void *user_data)
{
- struct bt_mesh_subnet *sub = user_data;
+ struct bt_mesh_subnet *sub = NULL;
+ u16_t net_idx = BLE_MESH_KEY_UNUSED;
BT_DBG("err %d", err);
+ net_idx = (u16_t)SNB_NET_IDX_GET(user_data);
+
+ /* For node, directly updating the "beacon_sent" timestamp is fine,
+ * since the subnet is pre-allocated.
+ * For Provisioner, before updating the "beacon_sent" timestamp, we
+ * need to make sure that the subnet still exists, because there is
+ * a chance that the subnet is removed just before the completion of
+ * sending the Secure Network Beacon.
+ */
+ if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) {
+ sub = bt_mesh_subnet_get(net_idx);
+ } else if (IS_ENABLED(CONFIG_BLE_MESH_PROVISIONER) &&
+ bt_mesh_is_provisioner_en()) {
+ sub = bt_mesh_provisioner_subnet_get(net_idx);
+ }
+
+ if (sub) {
sub->beacon_sent = k_uptime_get_32();
}
+}
void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
struct net_buf_simple *buf)
@@ -161,7 +183,17 @@ static int secure_beacon_send(void)
bt_mesh_beacon_create(sub, &buf->b);
- bt_mesh_adv_send(buf, &send_cb, sub);
+ /* Care should be taken here. Previously the user_data is the
+ * pointer of a subnet. When the device is a Provisioner, its
+ * subnet is created dynamically. If the corresponding subnet
+ * is removed right after the Secure Network Beacon is sent,
+ * update its "beacon_sent" timestamp in beacon_complete() will
+ * cause exception.
+ * Here we use the "net_idx" of the subnet instead. And in the
+ * beacon_complete(), we will try to get the subnet before
+ * updating its "beacon_sent" timestamp.
+ */
+ bt_mesh_adv_send(buf, &send_cb, SNB_NET_IDX_SET(sub->net_idx));
net_buf_unref(buf);
}
@@ -264,16 +296,9 @@ static void update_beacon_observation(void)
static bool ready_to_send(void)
{
- if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned()) {
- return true;
- }
-
- if (IS_ENABLED(CONFIG_BLE_MESH_PROVISIONER) && bt_mesh_is_provisioner_en()) {
- if (bt_mesh_provisioner_get_node_count()) {
+ if (bt_mesh_is_provisioned() || bt_mesh_is_provisioner_en()) {
return true;
}
- }
-
return false;
}
