1 /*
2  * Copyright (c) 2020 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/bluetooth/mesh.h>
7 #include <zephyr/sys/iterable_sections.h>
8 
9 #include "net.h"
10 #include "rpl.h"
11 #include "access.h"
12 #include "lpn.h"
13 #include "settings.h"
14 #include "mesh.h"
15 #include "transport.h"
16 #include "heartbeat.h"
17 #include "foundation.h"
18 
19 #define LOG_LEVEL CONFIG_BT_MESH_TRANS_LOG_LEVEL
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(bt_mesh_hb);
22 
23 /* Heartbeat Publication information for persistent storage. */
24 struct hb_pub_val {
25 	uint16_t dst;
26 	uint8_t  period;
27 	uint8_t  ttl;
28 	uint16_t feat;
29 	uint16_t net_idx:12,
30 		 indefinite:1;
31 };
32 
33 static struct bt_mesh_hb_pub pub;
34 static struct bt_mesh_hb_sub sub;
35 static struct k_work_delayable sub_timer;
36 static struct k_work_delayable pub_timer;
37 
notify_pub_sent(void)38 static void notify_pub_sent(void)
39 {
40 	STRUCT_SECTION_FOREACH(bt_mesh_hb_cb, cb) {
41 		if (cb->pub_sent) {
42 			cb->pub_sent(&pub);
43 		}
44 	}
45 }
46 
sub_remaining(void)47 static int64_t sub_remaining(void)
48 {
49 	if (sub.dst == BT_MESH_ADDR_UNASSIGNED) {
50 		return 0U;
51 	}
52 
53 	uint32_t rem_ms = k_ticks_to_ms_floor32(
54 		k_work_delayable_remaining_get(&sub_timer));
55 
56 	return rem_ms / MSEC_PER_SEC;
57 }
58 
hb_publish_end_cb(int err,void * cb_data)59 static void hb_publish_end_cb(int err, void *cb_data)
60 {
61 	if (pub.period && pub.count > 1) {
62 		k_work_reschedule(&pub_timer, K_SECONDS(pub.period));
63 	}
64 
65 	if (pub.count != 0xffff) {
66 		pub.count--;
67 	}
68 
69 	if (!err) {
70 		notify_pub_sent();
71 	}
72 }
73 
notify_recv(uint8_t hops,uint16_t feat)74 static void notify_recv(uint8_t hops, uint16_t feat)
75 {
76 	sub.remaining = sub_remaining();
77 
78 	STRUCT_SECTION_FOREACH(bt_mesh_hb_cb, cb) {
79 		if (cb->recv) {
80 			cb->recv(&sub, hops, feat);
81 		}
82 	}
83 }
84 
notify_sub_end(void)85 static void notify_sub_end(void)
86 {
87 	sub.remaining = 0;
88 
89 	STRUCT_SECTION_FOREACH(bt_mesh_hb_cb, cb) {
90 		if (cb->sub_end) {
91 			cb->sub_end(&sub);
92 		}
93 	}
94 }
95 
sub_end(struct k_work * work)96 static void sub_end(struct k_work *work)
97 {
98 	notify_sub_end();
99 }
100 
heartbeat_send(const struct bt_mesh_send_cb * cb,void * cb_data)101 static int heartbeat_send(const struct bt_mesh_send_cb *cb, void *cb_data)
102 {
103 	uint16_t feat = 0U;
104 	struct __packed {
105 		uint8_t init_ttl;
106 		uint16_t feat;
107 	} hb;
108 	struct bt_mesh_msg_ctx ctx = {
109 		.net_idx = pub.net_idx,
110 		.app_idx = BT_MESH_KEY_UNUSED,
111 		.addr = pub.dst,
112 		.send_ttl = pub.ttl,
113 	};
114 	struct bt_mesh_net_tx tx = {
115 		.sub = bt_mesh_subnet_get(pub.net_idx),
116 		.ctx = &ctx,
117 		.src = bt_mesh_primary_addr(),
118 		.xmit = bt_mesh_net_transmit_get(),
119 	};
120 
121 	/* Do nothing if heartbeat publication is not enabled or the subnet is
122 	 * removed.
123 	 */
124 	if (!tx.sub || pub.dst == BT_MESH_ADDR_UNASSIGNED) {
125 		return 0;
126 	}
127 
128 	hb.init_ttl = pub.ttl;
129 
130 	if (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED) {
131 		feat |= BT_MESH_FEAT_RELAY;
132 	}
133 
134 	if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
135 		feat |= BT_MESH_FEAT_PROXY;
136 	}
137 
138 	if (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED) {
139 		feat |= BT_MESH_FEAT_FRIEND;
140 	}
141 
142 	if (bt_mesh_lpn_established()) {
143 		feat |= BT_MESH_FEAT_LOW_POWER;
144 	}
145 
146 	hb.feat = sys_cpu_to_be16(feat);
147 
148 	LOG_DBG("InitTTL %u feat 0x%04x", pub.ttl, feat);
149 
150 	return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb),
151 				cb, cb_data);
152 }
153 
hb_publish_start_cb(uint16_t duration,int err,void * cb_data)154 static void hb_publish_start_cb(uint16_t duration, int err, void *cb_data)
155 {
156 	if (err) {
157 		hb_publish_end_cb(err, cb_data);
158 	}
159 }
160 
hb_publish(struct k_work * work)161 static void hb_publish(struct k_work *work)
162 {
163 	static const struct bt_mesh_send_cb publish_cb = {
164 		.start = hb_publish_start_cb,
165 		.end = hb_publish_end_cb,
166 	};
167 	struct bt_mesh_subnet *subnet;
168 	int err;
169 
170 	LOG_DBG("hb_pub.count: %u", pub.count);
171 
172 	/* Fast exit if disabled or expired */
173 	if (pub.period == 0U || pub.count == 0U) {
174 		return;
175 	}
176 
177 	subnet = bt_mesh_subnet_get(pub.net_idx);
178 	if (!subnet) {
179 		LOG_ERR("No matching subnet for idx 0x%02x", pub.net_idx);
180 		pub.dst = BT_MESH_ADDR_UNASSIGNED;
181 		return;
182 	}
183 
184 	err = heartbeat_send(&publish_cb, NULL);
185 	if (err) {
186 		hb_publish_end_cb(err, NULL);
187 	}
188 }
189 
bt_mesh_hb_recv(struct bt_mesh_net_rx * rx,struct net_buf_simple * buf)190 int bt_mesh_hb_recv(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
191 {
192 	uint8_t init_ttl, hops;
193 	uint16_t feat;
194 
195 	if (buf->len < 3) {
196 		LOG_ERR("Too short heartbeat message");
197 		return -EINVAL;
198 	}
199 
200 	init_ttl = (net_buf_simple_pull_u8(buf) & 0x7f);
201 	feat = net_buf_simple_pull_be16(buf);
202 
203 	hops = (init_ttl - rx->ctx.recv_ttl + 1);
204 
205 	if (rx->ctx.addr != sub.src || rx->ctx.recv_dst != sub.dst) {
206 		LOG_DBG("No subscription for received heartbeat");
207 		return 0;
208 	}
209 
210 	if (!k_work_delayable_is_pending(&sub_timer)) {
211 		LOG_DBG("Heartbeat subscription inactive");
212 		return 0;
213 	}
214 
215 	sub.min_hops = MIN(sub.min_hops, hops);
216 	sub.max_hops = MAX(sub.max_hops, hops);
217 
218 	if (sub.count < 0xffff) {
219 		sub.count++;
220 	}
221 
222 	LOG_DBG("src 0x%04x TTL %u InitTTL %u (%u hop%s) feat 0x%04x", rx->ctx.addr,
223 		rx->ctx.recv_ttl, init_ttl, hops, (hops == 1U) ? "" : "s", feat);
224 
225 	notify_recv(hops, feat);
226 
227 	return 0;
228 }
229 
pub_disable(void)230 static void pub_disable(void)
231 {
232 	LOG_DBG("");
233 
234 	pub.dst = BT_MESH_ADDR_UNASSIGNED;
235 	pub.count = 0U;
236 	pub.period = 0U;
237 	pub.ttl = 0U;
238 	pub.feat = 0U;
239 	pub.net_idx = 0U;
240 
241 	/* Try to cancel, but it's OK if this still runs (or is
242 	 * running) as the handler will be a no-op if it hasn't
243 	 * already checked period for being non-zero.
244 	 */
245 	(void)k_work_cancel_delayable(&pub_timer);
246 }
247 
bt_mesh_hb_pub_set(struct bt_mesh_hb_pub * new_pub)248 uint8_t bt_mesh_hb_pub_set(struct bt_mesh_hb_pub *new_pub)
249 {
250 	if (!new_pub || new_pub->dst == BT_MESH_ADDR_UNASSIGNED) {
251 		pub_disable();
252 
253 		if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
254 		    bt_mesh_is_provisioned()) {
255 			bt_mesh_settings_store_schedule(
256 					BT_MESH_SETTINGS_HB_PUB_PENDING);
257 		}
258 
259 		return STATUS_SUCCESS;
260 	}
261 
262 	if (!bt_mesh_subnet_get(new_pub->net_idx)) {
263 		LOG_ERR("Unknown NetKey 0x%04x", new_pub->net_idx);
264 		return STATUS_INVALID_NETKEY;
265 	}
266 
267 	new_pub->feat &= BT_MESH_FEAT_SUPPORTED;
268 	pub = *new_pub;
269 
270 	if (!bt_mesh_is_provisioned()) {
271 		return STATUS_SUCCESS;
272 	}
273 
274 	/* The first Heartbeat message shall be published as soon as possible
275 	 * after the Heartbeat Publication Period state has been configured for
276 	 * periodic publishing.
277 	 *
278 	 * If the new configuration disables publishing this flushes
279 	 * the work item.
280 	 */
281 	k_work_reschedule(&pub_timer, K_NO_WAIT);
282 
283 	if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
284 		bt_mesh_settings_store_schedule(
285 					BT_MESH_SETTINGS_HB_PUB_PENDING);
286 	}
287 
288 	return STATUS_SUCCESS;
289 }
290 
bt_mesh_hb_pub_get(struct bt_mesh_hb_pub * get)291 void bt_mesh_hb_pub_get(struct bt_mesh_hb_pub *get)
292 {
293 	*get = pub;
294 }
295 
bt_mesh_hb_sub_set(uint16_t src,uint16_t dst,uint32_t period)296 uint8_t bt_mesh_hb_sub_set(uint16_t src, uint16_t dst, uint32_t period)
297 {
298 	if (src != BT_MESH_ADDR_UNASSIGNED && !BT_MESH_ADDR_IS_UNICAST(src)) {
299 		LOG_WRN("Prohibited source address");
300 		return STATUS_INVALID_ADDRESS;
301 	}
302 
303 	if (BT_MESH_ADDR_IS_VIRTUAL(dst) || BT_MESH_ADDR_IS_RFU(dst) ||
304 	    (BT_MESH_ADDR_IS_UNICAST(dst) && dst != bt_mesh_primary_addr())) {
305 		LOG_WRN("Prohibited destination address");
306 		return STATUS_INVALID_ADDRESS;
307 	}
308 
309 	if (period > (1U << 16)) {
310 		LOG_WRN("Prohibited subscription period %u s", period);
311 		return STATUS_CANNOT_SET;
312 	}
313 
314 	/* Only an explicit address change to unassigned should trigger clearing
315 	 * of the values according to MESH/NODE/CFG/HBS/BV-02-C.
316 	 */
317 	if (src == BT_MESH_ADDR_UNASSIGNED || dst == BT_MESH_ADDR_UNASSIGNED) {
318 		sub.src = BT_MESH_ADDR_UNASSIGNED;
319 		sub.dst = BT_MESH_ADDR_UNASSIGNED;
320 		sub.min_hops = 0U;
321 		sub.max_hops = 0U;
322 		sub.count = 0U;
323 		sub.period = 0U;
324 	} else if (period) {
325 		sub.src = src;
326 		sub.dst = dst;
327 		sub.min_hops = BT_MESH_TTL_MAX;
328 		sub.max_hops = 0U;
329 		sub.count = 0U;
330 		sub.period = period;
331 	} else {
332 		/* Clearing the period should stop heartbeat subscription
333 		 * without clearing the parameters, so we can still read them.
334 		 */
335 		sub.period = 0U;
336 	}
337 
338 	/* Start the timer, which notifies immediately if the new
339 	 * configuration disables the subscription.
340 	 */
341 	k_work_reschedule(&sub_timer, K_SECONDS(sub.period));
342 
343 	return STATUS_SUCCESS;
344 }
345 
bt_mesh_hb_sub_reset_count(void)346 void bt_mesh_hb_sub_reset_count(void)
347 {
348 	sub.count = 0;
349 }
350 
bt_mesh_hb_sub_get(struct bt_mesh_hb_sub * get)351 void bt_mesh_hb_sub_get(struct bt_mesh_hb_sub *get)
352 {
353 	*get = sub;
354 	get->remaining = sub_remaining();
355 }
356 
hb_unsolicited_pub_end_cb(int err,void * cb_data)357 static void hb_unsolicited_pub_end_cb(int err, void *cb_data)
358 {
359 	if (!err) {
360 		notify_pub_sent();
361 	}
362 }
363 
bt_mesh_hb_feature_changed(uint16_t features)364 void bt_mesh_hb_feature_changed(uint16_t features)
365 {
366 	static const struct bt_mesh_send_cb pub_cb = {
367 		.end = hb_unsolicited_pub_end_cb,
368 	};
369 
370 	if (pub.dst == BT_MESH_ADDR_UNASSIGNED) {
371 		return;
372 	}
373 
374 	if (!(pub.feat & features)) {
375 		return;
376 	}
377 
378 	heartbeat_send(&pub_cb, NULL);
379 }
380 
bt_mesh_hb_init(void)381 void bt_mesh_hb_init(void)
382 {
383 	pub.net_idx = BT_MESH_KEY_UNUSED;
384 	k_work_init_delayable(&pub_timer, hb_publish);
385 	k_work_init_delayable(&sub_timer, sub_end);
386 }
387 
bt_mesh_hb_start(void)388 void bt_mesh_hb_start(void)
389 {
390 	if (pub.count && pub.period) {
391 		LOG_DBG("Starting heartbeat publication");
392 		k_work_reschedule(&pub_timer, K_NO_WAIT);
393 	}
394 }
395 
bt_mesh_hb_suspend(void)396 void bt_mesh_hb_suspend(void)
397 {
398 	/* Best-effort suspend.  This cannot guarantee that an
399 	 * in-progress publish will not complete.
400 	 */
401 	(void)k_work_cancel_delayable(&pub_timer);
402 }
403 
bt_mesh_hb_resume(void)404 void bt_mesh_hb_resume(void)
405 {
406 	if (pub.period && pub.count) {
407 		LOG_DBG("Starting heartbeat publication");
408 		k_work_reschedule(&pub_timer, K_NO_WAIT);
409 	}
410 }
411 
hb_pub_set(const char * name,size_t len_rd,settings_read_cb read_cb,void * cb_arg)412 static int hb_pub_set(const char *name, size_t len_rd,
413 		      settings_read_cb read_cb, void *cb_arg)
414 {
415 	struct bt_mesh_hb_pub hb_pub;
416 	struct hb_pub_val hb_val;
417 	int err;
418 
419 	err = bt_mesh_settings_set(read_cb, cb_arg, &hb_val, sizeof(hb_val));
420 	if (err) {
421 		LOG_ERR("Failed to set \'hb_val\'");
422 		return err;
423 	}
424 
425 	hb_pub.dst = hb_val.dst;
426 	hb_pub.period = bt_mesh_hb_pwr2(hb_val.period);
427 	hb_pub.ttl = hb_val.ttl;
428 	hb_pub.feat = hb_val.feat;
429 	hb_pub.net_idx = hb_val.net_idx;
430 
431 	if (hb_val.indefinite) {
432 		hb_pub.count = 0xffff;
433 	} else {
434 		hb_pub.count = 0U;
435 	}
436 
437 	(void)bt_mesh_hb_pub_set(&hb_pub);
438 
439 	LOG_DBG("Restored heartbeat publication");
440 
441 	return 0;
442 }
443 
444 BT_MESH_SETTINGS_DEFINE(pub, "HBPub", hb_pub_set);
445 
bt_mesh_hb_pub_pending_store(void)446 void bt_mesh_hb_pub_pending_store(void)
447 {
448 	struct bt_mesh_hb_pub hb_pub;
449 	struct hb_pub_val val;
450 	int err;
451 
452 	bt_mesh_hb_pub_get(&hb_pub);
453 	if (hb_pub.dst == BT_MESH_ADDR_UNASSIGNED) {
454 		err = settings_delete("bt/mesh/HBPub");
455 	} else {
456 		val.indefinite = (hb_pub.count == 0xffff);
457 		val.dst = hb_pub.dst;
458 		val.period = bt_mesh_hb_log(hb_pub.period);
459 		val.ttl = hb_pub.ttl;
460 		val.feat = hb_pub.feat;
461 		val.net_idx = hb_pub.net_idx;
462 
463 		err = settings_save_one("bt/mesh/HBPub", &val, sizeof(val));
464 	}
465 
466 	if (err) {
467 		LOG_ERR("Failed to store Heartbeat Publication");
468 	} else {
469 		LOG_DBG("Stored Heartbeat Publication");
470 	}
471 }
472