1 /*
2 * Copyright (c) 2017 Intel Corporation
3 * Copyright (c) 2020 Lingao Meng
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <stdbool.h>
12 #include <stdlib.h>
13 #include <sys/atomic.h>
14 #include <sys/util.h>
15 #include <sys/byteorder.h>
16
17 #include <net/buf.h>
18 #include <bluetooth/bluetooth.h>
19 #include <bluetooth/conn.h>
20 #include <bluetooth/mesh.h>
21
22 #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_RPL)
23 #define LOG_MODULE_NAME bt_mesh_rpl
24 #include "common/log.h"
25
26 #include "mesh.h"
27 #include "adv.h"
28 #include "net.h"
29 #include "rpl.h"
30 #include "settings.h"
31
32 /* Replay Protection List information for persistent storage. */
33 struct rpl_val {
34 uint32_t seq:24,
35 old_iv:1;
36 };
37
38 static struct bt_mesh_rpl replay_list[CONFIG_BT_MESH_CRPL];
39 static ATOMIC_DEFINE(store, CONFIG_BT_MESH_CRPL);
40
rpl_idx(const struct bt_mesh_rpl * rpl)41 static inline int rpl_idx(const struct bt_mesh_rpl *rpl)
42 {
43 return rpl - &replay_list[0];
44 }
45
clear_rpl(struct bt_mesh_rpl * rpl)46 static void clear_rpl(struct bt_mesh_rpl *rpl)
47 {
48 int err;
49 char path[18];
50
51 if (!rpl->src) {
52 return;
53 }
54
55 snprintk(path, sizeof(path), "bt/mesh/RPL/%x", rpl->src);
56 err = settings_delete(path);
57 if (err) {
58 BT_ERR("Failed to clear RPL");
59 } else {
60 BT_DBG("Cleared RPL");
61 }
62
63 (void)memset(rpl, 0, sizeof(*rpl));
64 atomic_clear_bit(store, rpl_idx(rpl));
65 }
66
schedule_rpl_store(struct bt_mesh_rpl * entry,bool force)67 static void schedule_rpl_store(struct bt_mesh_rpl *entry, bool force)
68 {
69 atomic_set_bit(store, rpl_idx(entry));
70
71 if (force
72 #ifdef CONFIG_BT_MESH_RPL_STORE_TIMEOUT
73 || CONFIG_BT_MESH_RPL_STORE_TIMEOUT >= 0
74 #endif
75 ) {
76 bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_RPL_PENDING);
77 }
78 }
79
schedule_rpl_clear(void)80 static void schedule_rpl_clear(void)
81 {
82 bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_RPL_PENDING);
83 }
84
bt_mesh_rpl_update(struct bt_mesh_rpl * rpl,struct bt_mesh_net_rx * rx)85 void bt_mesh_rpl_update(struct bt_mesh_rpl *rpl,
86 struct bt_mesh_net_rx *rx)
87 {
88 /* If this is the first message on the new IV index, we should reset it
89 * to zero to avoid invalid combinations of IV index and seg.
90 */
91 if (rpl->old_iv && !rx->old_iv) {
92 rpl->seg = 0;
93 }
94
95 rpl->src = rx->ctx.addr;
96 rpl->seq = rx->seq;
97 rpl->old_iv = rx->old_iv;
98
99 if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
100 schedule_rpl_store(rpl, false);
101 }
102 }
103
104 /* Check the Replay Protection List for a replay attempt. If non-NULL match
105 * parameter is given the RPL slot is returned but it is not immediately
106 * updated (needed for segmented messages), whereas if a NULL match is given
107 * the RPL is immediately updated (used for unsegmented messages).
108 */
bt_mesh_rpl_check(struct bt_mesh_net_rx * rx,struct bt_mesh_rpl ** match)109 bool bt_mesh_rpl_check(struct bt_mesh_net_rx *rx,
110 struct bt_mesh_rpl **match)
111 {
112 int i;
113
114 /* Don't bother checking messages from ourselves */
115 if (rx->net_if == BT_MESH_NET_IF_LOCAL) {
116 return false;
117 }
118
119 /* The RPL is used only for the local node */
120 if (!rx->local_match) {
121 return false;
122 }
123
124 for (i = 0; i < ARRAY_SIZE(replay_list); i++) {
125 struct bt_mesh_rpl *rpl = &replay_list[i];
126
127 /* Empty slot */
128 if (!rpl->src) {
129 if (match) {
130 *match = rpl;
131 } else {
132 bt_mesh_rpl_update(rpl, rx);
133 }
134
135 return false;
136 }
137
138 /* Existing slot for given address */
139 if (rpl->src == rx->ctx.addr) {
140 if (rx->old_iv && !rpl->old_iv) {
141 return true;
142 }
143
144 if ((!rx->old_iv && rpl->old_iv) ||
145 rpl->seq < rx->seq) {
146 if (match) {
147 *match = rpl;
148 } else {
149 bt_mesh_rpl_update(rpl, rx);
150 }
151
152 return false;
153 } else {
154 return true;
155 }
156 }
157 }
158
159 BT_ERR("RPL is full!");
160 return true;
161 }
162
bt_mesh_rpl_clear(void)163 void bt_mesh_rpl_clear(void)
164 {
165 BT_DBG("");
166
167 if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
168 schedule_rpl_clear();
169 } else {
170 (void)memset(replay_list, 0, sizeof(replay_list));
171 }
172 }
173
bt_mesh_rpl_find(uint16_t src)174 static struct bt_mesh_rpl *bt_mesh_rpl_find(uint16_t src)
175 {
176 int i;
177
178 for (i = 0; i < ARRAY_SIZE(replay_list); i++) {
179 if (replay_list[i].src == src) {
180 return &replay_list[i];
181 }
182 }
183
184 return NULL;
185 }
186
bt_mesh_rpl_alloc(uint16_t src)187 static struct bt_mesh_rpl *bt_mesh_rpl_alloc(uint16_t src)
188 {
189 int i;
190
191 for (i = 0; i < ARRAY_SIZE(replay_list); i++) {
192 if (!replay_list[i].src) {
193 replay_list[i].src = src;
194 return &replay_list[i];
195 }
196 }
197
198 return NULL;
199 }
200
bt_mesh_rpl_reset(void)201 void bt_mesh_rpl_reset(void)
202 {
203 int i;
204
205 /* Discard "old old" IV Index entries from RPL and flag
206 * any other ones (which are valid) as old.
207 */
208 for (i = 0; i < ARRAY_SIZE(replay_list); i++) {
209 struct bt_mesh_rpl *rpl = &replay_list[i];
210
211 if (rpl->src) {
212 if (rpl->old_iv) {
213 if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
214 clear_rpl(rpl);
215 } else {
216 (void)memset(rpl, 0, sizeof(*rpl));
217 }
218 } else {
219 rpl->old_iv = true;
220
221 if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
222 schedule_rpl_store(rpl, true);
223 }
224 }
225 }
226 }
227 }
228
rpl_set(const char * name,size_t len_rd,settings_read_cb read_cb,void * cb_arg)229 static int rpl_set(const char *name, size_t len_rd,
230 settings_read_cb read_cb, void *cb_arg)
231 {
232 struct bt_mesh_rpl *entry;
233 struct rpl_val rpl;
234 int err;
235 uint16_t src;
236
237 if (!name) {
238 BT_ERR("Insufficient number of arguments");
239 return -ENOENT;
240 }
241
242 src = strtol(name, NULL, 16);
243 entry = bt_mesh_rpl_find(src);
244
245 if (len_rd == 0) {
246 BT_DBG("val (null)");
247 if (entry) {
248 (void)memset(entry, 0, sizeof(*entry));
249 } else {
250 BT_WARN("Unable to find RPL entry for 0x%04x", src);
251 }
252
253 return 0;
254 }
255
256 if (!entry) {
257 entry = bt_mesh_rpl_alloc(src);
258 if (!entry) {
259 BT_ERR("Unable to allocate RPL entry for 0x%04x", src);
260 return -ENOMEM;
261 }
262 }
263
264 err = bt_mesh_settings_set(read_cb, cb_arg, &rpl, sizeof(rpl));
265 if (err) {
266 BT_ERR("Failed to set `net`");
267 return err;
268 }
269
270 entry->seq = rpl.seq;
271 entry->old_iv = rpl.old_iv;
272
273 BT_DBG("RPL entry for 0x%04x: Seq 0x%06x old_iv %u", entry->src,
274 entry->seq, entry->old_iv);
275
276 return 0;
277 }
278
279 BT_MESH_SETTINGS_DEFINE(rpl, "RPL", rpl_set);
280
store_rpl(struct bt_mesh_rpl * entry)281 static void store_rpl(struct bt_mesh_rpl *entry)
282 {
283 struct rpl_val rpl;
284 char path[18];
285 int err;
286
287 if (!entry->src) {
288 return;
289 }
290
291 BT_DBG("src 0x%04x seq 0x%06x old_iv %u", entry->src, entry->seq,
292 entry->old_iv);
293
294 rpl.seq = entry->seq;
295 rpl.old_iv = entry->old_iv;
296
297 snprintk(path, sizeof(path), "bt/mesh/RPL/%x", entry->src);
298
299 err = settings_save_one(path, &rpl, sizeof(rpl));
300 if (err) {
301 BT_ERR("Failed to store RPL %s value", log_strdup(path));
302 } else {
303 BT_DBG("Stored RPL %s value", log_strdup(path));
304 }
305 }
306
store_pending_rpl(struct bt_mesh_rpl * rpl)307 static void store_pending_rpl(struct bt_mesh_rpl *rpl)
308 {
309 BT_DBG("");
310
311 if (atomic_test_and_clear_bit(store, rpl_idx(rpl))) {
312 store_rpl(rpl);
313 }
314 }
315
bt_mesh_rpl_pending_store(uint16_t addr)316 void bt_mesh_rpl_pending_store(uint16_t addr)
317 {
318 int i;
319
320 if (!IS_ENABLED(CONFIG_BT_SETTINGS) ||
321 (!BT_MESH_ADDR_IS_UNICAST(addr) &&
322 addr != BT_MESH_ADDR_ALL_NODES)) {
323 return;
324 }
325
326 if (addr == BT_MESH_ADDR_ALL_NODES) {
327 bt_mesh_settings_store_cancel(BT_MESH_SETTINGS_RPL_PENDING);
328 }
329
330 for (i = 0; i < ARRAY_SIZE(replay_list); i++) {
331 if (addr != BT_MESH_ADDR_ALL_NODES &&
332 addr != replay_list[i].src) {
333 continue;
334 }
335
336 if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) {
337 store_pending_rpl(&replay_list[i]);
338 } else {
339 clear_rpl(&replay_list[i]);
340 }
341
342 if (addr != BT_MESH_ADDR_ALL_NODES) {
343 break;
344 }
345 }
346 }
347