1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/mesh.h>
8 #include <zephyr/bluetooth/mesh/brg_cfg.h>
9 #include "access.h"
10 #include "brg_cfg.h"
11 #include "foundation.h"
12 #include "subnet.h"
13 
14 #define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(bt_mesh_brg_cfg_srv);
17 
bridge_status_send(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx)18 static void bridge_status_send(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx)
19 {
20 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_SUBNET_BRIDGE_STATUS, 1);
21 
22 	bt_mesh_model_msg_init(&msg, OP_SUBNET_BRIDGE_STATUS);
23 	net_buf_simple_add_u8(&msg, bt_mesh_brg_cfg_enable_get() ? 1 : 0);
24 
25 	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
26 		LOG_ERR("Brg Status send failed");
27 	}
28 }
29 
subnet_bridge_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)30 static int subnet_bridge_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
31 			     struct net_buf_simple *buf)
32 {
33 	bridge_status_send(model, ctx);
34 
35 	return 0;
36 }
37 
subnet_bridge_set(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)38 static int subnet_bridge_set(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
39 			     struct net_buf_simple *buf)
40 {
41 	uint8_t enable = net_buf_simple_pull_u8(buf);
42 
43 	if (enable > BT_MESH_BRG_CFG_ENABLED) {
44 		return -EINVAL;
45 	}
46 
47 	bt_mesh_brg_cfg_enable_set(enable);
48 	bridge_status_send(model, ctx);
49 
50 	return 0;
51 }
52 
bridging_table_status_send(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,uint8_t status,struct bt_mesh_brg_cfg_table_entry * entry)53 static void bridging_table_status_send(const struct bt_mesh_model *model,
54 				       struct bt_mesh_msg_ctx *ctx, uint8_t status,
55 				       struct bt_mesh_brg_cfg_table_entry *entry)
56 {
57 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_STATUS, 9);
58 
59 	bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_STATUS);
60 	net_buf_simple_add_u8(&msg, status);
61 	net_buf_simple_add_u8(&msg, entry->directions);
62 	key_idx_pack_pair(&msg, entry->net_idx1, entry->net_idx2);
63 	net_buf_simple_add_le16(&msg, entry->addr1);
64 	net_buf_simple_add_le16(&msg, entry->addr2);
65 
66 	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
67 		LOG_ERR("Brg Tbl Status send failed");
68 	}
69 }
70 
netkey_check(uint16_t net_idx1,uint16_t net_idx2)71 static bool netkey_check(uint16_t net_idx1, uint16_t net_idx2)
72 {
73 	return bt_mesh_subnet_get(net_idx1) && bt_mesh_subnet_get(net_idx2);
74 }
75 
bridging_table_add(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)76 static int bridging_table_add(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
77 			      struct net_buf_simple *buf)
78 {
79 	struct bt_mesh_brg_cfg_table_entry entry;
80 	uint8_t status = STATUS_SUCCESS;
81 	int err;
82 
83 	entry.directions = net_buf_simple_pull_u8(buf);
84 	key_idx_unpack_pair(buf, &entry.net_idx1, &entry.net_idx2);
85 	entry.addr1 = net_buf_simple_pull_le16(buf);
86 	entry.addr2 = net_buf_simple_pull_le16(buf);
87 
88 	err = bt_mesh_brg_cfg_tbl_add(entry.directions, entry.net_idx1, entry.net_idx2, entry.addr1,
89 				      entry.addr2, &status);
90 	if (err) {
91 		return err;
92 	}
93 
94 	bridging_table_status_send(model, ctx, status, &entry);
95 
96 	return 0;
97 }
98 
bridging_table_remove(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)99 static int bridging_table_remove(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
100 				 struct net_buf_simple *buf)
101 {
102 	struct bt_mesh_brg_cfg_table_entry entry;
103 	uint8_t status = STATUS_SUCCESS;
104 	int err;
105 
106 	entry.directions = 0;
107 	key_idx_unpack_pair(buf, &entry.net_idx1, &entry.net_idx2);
108 	entry.addr1 = net_buf_simple_pull_le16(buf);
109 	entry.addr2 = net_buf_simple_pull_le16(buf);
110 
111 	err = bt_mesh_brg_cfg_tbl_remove(entry.net_idx1, entry.net_idx2, entry.addr1, entry.addr2,
112 					 &status);
113 	if (err) {
114 		return err;
115 	}
116 
117 	bridging_table_status_send(model, ctx, status, &entry);
118 
119 	return 0;
120 }
121 
bridged_subnets_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)122 static int bridged_subnets_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
123 			       struct net_buf_simple *buf)
124 {
125 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGED_SUBNETS_LIST,
126 				 BT_MESH_TX_SDU_MAX -
127 					 BT_MESH_MODEL_OP_LEN(OP_BRIDGED_SUBNETS_LIST));
128 	bt_mesh_model_msg_init(&msg, OP_BRIDGED_SUBNETS_LIST);
129 
130 	const struct bt_mesh_brg_cfg_row *brg_tbl;
131 	int rows = bt_mesh_brg_cfg_tbl_get(&brg_tbl);
132 	int16_t net_idx_filter = net_buf_simple_pull_le16(buf);
133 
134 	if (net_idx_filter & BT_MESH_BRG_CFG_NKEY_PRHB_FLT_MASK) {
135 		return -EINVAL;
136 	}
137 
138 	struct bt_mesh_brg_cfg_filter_netkey filter_net_idx;
139 
140 	filter_net_idx.filter = net_idx_filter & BIT_MASK(2);
141 	filter_net_idx.net_idx = (net_idx_filter >> 4) & BIT_MASK(12);
142 
143 	uint8_t start_id = net_buf_simple_pull_u8(buf);
144 
145 	net_buf_simple_add_le16(&msg, net_idx_filter);
146 	net_buf_simple_add_u8(&msg, start_id);
147 
148 	uint8_t cnt = 0;
149 	uint16_t net_idx1, net_idx2;
150 
151 	for (int i = 0; i < rows; i++) {
152 		net_idx1 = brg_tbl[i].net_idx1;
153 		net_idx2 = brg_tbl[i].net_idx2;
154 
155 		if (net_buf_simple_tailroom(&msg) < 3 + BT_MESH_MIC_SHORT) {
156 			break;
157 		}
158 
159 		switch (filter_net_idx.filter) {
160 		/* Report pair of NetKeys from the table, starting from start_id. */
161 		case 0:
162 			if (i >= start_id) {
163 				key_idx_pack_pair(&msg, net_idx1, net_idx2);
164 			}
165 			break;
166 
167 		/* Report pair of NetKeys in which (NetKeyIndex1) matches the net_idx */
168 		case 1:
169 			if (net_idx1 == filter_net_idx.net_idx) {
170 				if (cnt >= start_id) {
171 					key_idx_pack_pair(&msg, net_idx1, net_idx2);
172 				}
173 				cnt++;
174 			}
175 			break;
176 
177 		/* Report pair of NetKeys in which (NetKeyIndex2) matches the net_idx */
178 		case 2:
179 			if (net_idx2 == filter_net_idx.net_idx) {
180 				if (cnt >= start_id) {
181 					key_idx_pack_pair(&msg, net_idx1, net_idx2);
182 				}
183 				cnt++;
184 			}
185 			break;
186 
187 		/* Report pair of NetKeys in which (NetKeyIndex1 or NetKeyIndex2) matches the
188 		 * net_idx
189 		 */
190 		case 3:
191 			if (net_idx1 == filter_net_idx.net_idx ||
192 			    net_idx2 == filter_net_idx.net_idx) {
193 				if (cnt >= start_id) {
194 					key_idx_pack_pair(&msg, net_idx1, net_idx2);
195 				}
196 				cnt++;
197 			}
198 			break;
199 
200 		default:
201 			CODE_UNREACHABLE;
202 		}
203 	}
204 
205 	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
206 		LOG_ERR("Brg Subnet List send failed");
207 	}
208 
209 	return 0;
210 }
211 
bridging_table_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)212 static int bridging_table_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
213 			      struct net_buf_simple *buf)
214 {
215 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_LIST,
216 				 BT_MESH_TX_SDU_MAX - BT_MESH_MODEL_OP_LEN(OP_BRIDGING_TABLE_LIST));
217 	uint8_t status = STATUS_SUCCESS;
218 	uint16_t net_idx1, net_idx2;
219 
220 	bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_LIST);
221 
222 	key_idx_unpack_pair(buf, &net_idx1, &net_idx2);
223 
224 	uint16_t start_id = net_buf_simple_pull_le16(buf);
225 
226 	if (!netkey_check(net_idx1, net_idx2)) {
227 		status = STATUS_INVALID_NETKEY;
228 	}
229 
230 	net_buf_simple_add_u8(&msg, status);
231 	key_idx_pack_pair(&msg, net_idx1, net_idx2);
232 	net_buf_simple_add_le16(&msg, start_id);
233 
234 	if (status != STATUS_SUCCESS) {
235 		goto tbl_get_respond;
236 	}
237 
238 	int cnt = 0;
239 	const struct bt_mesh_brg_cfg_row *brg_tbl;
240 	int rows = bt_mesh_brg_cfg_tbl_get(&brg_tbl);
241 
242 	for (int i = 0; i < rows; i++) {
243 		if (brg_tbl[i].net_idx1 == net_idx1 && brg_tbl[i].net_idx2 == net_idx2) {
244 			if (cnt >= start_id) {
245 				if (net_buf_simple_tailroom(&msg) < 5 + BT_MESH_MIC_SHORT) {
246 					LOG_WRN("Bridging Table List message too large");
247 					break;
248 				}
249 
250 				net_buf_simple_add_le16(&msg, brg_tbl[i].addr1);
251 				net_buf_simple_add_le16(&msg, brg_tbl[i].addr2);
252 				net_buf_simple_add_u8(&msg, brg_tbl[i].direction);
253 			}
254 			cnt++;
255 		}
256 	}
257 
258 tbl_get_respond:
259 	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
260 		LOG_ERR("Brg Tbl List send failed");
261 	}
262 
263 	return 0;
264 }
265 
bridging_table_size_get(const struct bt_mesh_model * model,struct bt_mesh_msg_ctx * ctx,struct net_buf_simple * buf)266 static int bridging_table_size_get(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
267 				   struct net_buf_simple *buf)
268 {
269 	BT_MESH_MODEL_BUF_DEFINE(msg, OP_BRIDGING_TABLE_SIZE_STATUS, 2);
270 	bt_mesh_model_msg_init(&msg, OP_BRIDGING_TABLE_SIZE_STATUS);
271 
272 	net_buf_simple_add_le16(&msg, CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX);
273 
274 	if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
275 		LOG_ERR("Brg Tbl Size Status send failed");
276 	}
277 
278 	return 0;
279 }
280 
281 const struct bt_mesh_model_op _bt_mesh_brg_cfg_srv_op[] = {
282 	{OP_SUBNET_BRIDGE_GET, BT_MESH_LEN_EXACT(0), subnet_bridge_get},
283 	{OP_SUBNET_BRIDGE_SET, BT_MESH_LEN_EXACT(1), subnet_bridge_set},
284 	{OP_BRIDGING_TABLE_ADD, BT_MESH_LEN_EXACT(8), bridging_table_add},
285 	{OP_BRIDGING_TABLE_REMOVE, BT_MESH_LEN_EXACT(7), bridging_table_remove},
286 	{OP_BRIDGED_SUBNETS_GET, BT_MESH_LEN_EXACT(3), bridged_subnets_get},
287 	{OP_BRIDGING_TABLE_GET, BT_MESH_LEN_EXACT(5), bridging_table_get},
288 	{OP_BRIDGING_TABLE_SIZE_GET, BT_MESH_LEN_EXACT(0), bridging_table_size_get},
289 	BT_MESH_MODEL_OP_END,
290 };
291 
brg_cfg_srv_init(const struct bt_mesh_model * model)292 static int brg_cfg_srv_init(const struct bt_mesh_model *model)
293 {
294 	const struct bt_mesh_model *config_srv =
295 		bt_mesh_model_find(bt_mesh_model_elem(model), BT_MESH_MODEL_ID_CFG_SRV);
296 
297 	if (config_srv == NULL) {
298 		LOG_ERR("Not on primary element");
299 		return -EINVAL;
300 	}
301 
302 	/*
303 	 * Bridge Configuration Server model security is device key based and only the local
304 	 * device key is allowed to access this model.
305 	 */
306 	model->keys[0] = BT_MESH_KEY_DEV_LOCAL;
307 	model->rt->flags |= BT_MESH_MOD_DEVKEY_ONLY;
308 
309 	bt_mesh_model_extend(model, config_srv);
310 
311 	return 0;
312 }
313 
brg_cfg_srv_reset(const struct bt_mesh_model * model)314 void brg_cfg_srv_reset(const struct bt_mesh_model *model)
315 {
316 	bt_mesh_brg_cfg_tbl_reset();
317 }
318 
319 const struct bt_mesh_model_cb _bt_mesh_brg_cfg_srv_cb = {
320 	.init = brg_cfg_srv_init,
321 	.reset = brg_cfg_srv_reset,
322 };
323