1 /*
2 * Copyright (c) 2021 Xiaomi Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/byteorder.h>
9
10 #include <zephyr/net/buf.h>
11 #include <zephyr/bluetooth/bluetooth.h>
12 #include <zephyr/bluetooth/hci.h>
13 #include <zephyr/bluetooth/uuid.h>
14 #include <zephyr/bluetooth/conn.h>
15 #include <zephyr/bluetooth/gatt.h>
16 #include <zephyr/bluetooth/mesh.h>
17
18 #include "mesh.h"
19 #include "net.h"
20 #include "rpl.h"
21 #include "transport.h"
22 #include "prov.h"
23 #include "beacon.h"
24 #include "foundation.h"
25 #include "access.h"
26 #include "proxy.h"
27 #include "gatt_cli.h"
28 #include "proxy_msg.h"
29 #include "crypto.h"
30
31 #define LOG_LEVEL CONFIG_BT_MESH_PROXY_LOG_LEVEL
32 #include <zephyr/logging/log.h>
33 LOG_MODULE_REGISTER(bt_mesh_proxy_client);
34
35 static struct bt_mesh_proxy_server {
36 struct bt_mesh_proxy_role *role;
37 bool link_opened;
38 uint16_t net_idx;
39 } servers[CONFIG_BT_MAX_CONN] = {
40 [0 ... (CONFIG_BT_MAX_CONN - 1)] = {
41 .net_idx = BT_MESH_KEY_UNUSED,
42 },
43 };
44
45 static bool allow_all_subnet;
46
find_proxy_srv(uint16_t net_idx,bool conn,bool disconn)47 static struct bt_mesh_proxy_server *find_proxy_srv(uint16_t net_idx,
48 bool conn, bool disconn)
49 {
50 for (int i = 0; i < ARRAY_SIZE(servers); i++) {
51 if (!servers[i].role) {
52 if (!disconn) {
53 continue;
54 }
55 } else if (!conn) {
56 continue;
57 }
58
59 if (servers[i].net_idx == net_idx) {
60 return &servers[i];
61 }
62 }
63
64 return NULL;
65 }
66
find_proxy_srv_by_conn(struct bt_conn * conn)67 static struct bt_mesh_proxy_server *find_proxy_srv_by_conn(struct bt_conn *conn)
68 {
69 for (int i = 0; i < ARRAY_SIZE(servers); i++) {
70 if (!servers[i].role ||
71 servers[i].role->conn != conn) {
72 continue;
73 }
74
75 return &servers[i];
76 }
77
78 return NULL;
79 }
80
bt_mesh_proxy_cli_relay(struct bt_mesh_adv * adv)81 bool bt_mesh_proxy_cli_relay(struct bt_mesh_adv *adv)
82 {
83 bool relayed = false;
84 int i;
85
86 for (i = 0; i < ARRAY_SIZE(servers); i++) {
87 struct bt_mesh_proxy_server *server = &servers[i];
88
89 if (!server->link_opened) {
90 continue;
91 }
92
93 if (bt_mesh_proxy_relay_send(server->role->conn, adv)) {
94 continue;
95 }
96
97 relayed = true;
98 }
99
100 return relayed;
101 }
102
proxy_msg_recv(struct bt_mesh_proxy_role * role)103 static void proxy_msg_recv(struct bt_mesh_proxy_role *role)
104 {
105 switch (role->msg_type) {
106 case BT_MESH_PROXY_NET_PDU:
107 LOG_DBG("Mesh Network PDU");
108 bt_mesh_net_recv(&role->buf, 0, BT_MESH_NET_IF_PROXY);
109 break;
110 case BT_MESH_PROXY_BEACON:
111 LOG_DBG("Mesh Beacon PDU");
112 bt_mesh_beacon_recv(&role->buf);
113 break;
114 case BT_MESH_PROXY_CONFIG:
115 LOG_DBG("Mesh Configuration PDU");
116 /* TODO */
117 break;
118 default:
119 LOG_WRN("Unhandled Message Type 0x%02x", role->msg_type);
120 break;
121 }
122 }
123
proxy_connected(struct bt_conn * conn,void * user_data)124 static void proxy_connected(struct bt_conn *conn, void *user_data)
125 {
126 struct bt_mesh_proxy_server *srv = user_data;
127
128 srv->role = bt_mesh_proxy_role_setup(conn, bt_mesh_gatt_send,
129 proxy_msg_recv);
130 }
131
proxy_link_open(struct bt_conn * conn)132 static void proxy_link_open(struct bt_conn *conn)
133 {
134 struct bt_mesh_proxy_server *srv = find_proxy_srv_by_conn(conn);
135
136 srv->link_opened = true;
137 }
138
proxy_disconnected(struct bt_conn * conn)139 static void proxy_disconnected(struct bt_conn *conn)
140 {
141 struct bt_mesh_proxy_server *srv = find_proxy_srv_by_conn(conn);
142
143 bt_mesh_proxy_role_cleanup(srv->role);
144
145 srv->role = NULL;
146 srv->link_opened = false;
147 }
148
149 static const struct bt_mesh_gatt_cli proxy = {
150 .srv_uuid = BT_UUID_INIT_16(BT_UUID_MESH_PROXY_VAL),
151 .data_in_uuid = BT_UUID_INIT_16(BT_UUID_MESH_PROXY_DATA_IN_VAL),
152 .data_out_uuid = BT_UUID_INIT_16(BT_UUID_MESH_PROXY_DATA_OUT_VAL),
153 .data_out_cccd_uuid = BT_UUID_INIT_16(BT_UUID_GATT_CCC_VAL),
154
155 .connected = proxy_connected,
156 .link_open = proxy_link_open,
157 .disconnected = proxy_disconnected
158 };
159
proxy_srv_check_and_get(struct bt_mesh_subnet * sub,const uint8_t * net_id,struct bt_mesh_proxy_server ** p_srv)160 static bool proxy_srv_check_and_get(struct bt_mesh_subnet *sub, const uint8_t *net_id,
161 struct bt_mesh_proxy_server **p_srv)
162 {
163 struct bt_mesh_proxy_server *srv;
164
165 srv = find_proxy_srv(sub->net_idx, true, true);
166 if (srv) {
167 if (srv->role) {
168 return true;
169 }
170 } else if (!allow_all_subnet) {
171 return false;
172 }
173
174 if (!srv) {
175 srv = find_proxy_srv(BT_MESH_KEY_UNUSED, false, true);
176 if (!srv) {
177 return true;
178 }
179 }
180
181 /* If net_id is NULL we already know that the networks match */
182 if (!net_id || !memcmp(sub->keys[0].net_id, net_id, 8) ||
183 (bt_mesh_subnet_has_new_key(sub) && !memcmp(sub->keys[1].net_id, net_id, 8))) {
184
185 *p_srv = srv;
186 return true;
187 }
188
189 return false;
190 }
191
192 struct find_net_id {
193 uint8_t type;
194
195 union {
196 const uint8_t *net_id;
197 struct {
198 const uint8_t *hash;
199 const uint8_t *rand;
200 } priv;
201 } data;
202
203 struct bt_mesh_proxy_server *srv;
204 };
205
is_hash_equal(struct bt_mesh_subnet * sub,struct find_net_id * res,uint8_t idx)206 static bool is_hash_equal(struct bt_mesh_subnet *sub, struct find_net_id *res, uint8_t idx)
207 {
208 int err;
209 uint8_t in[16], out[16];
210
211 memcpy(&in[0], sub->keys[idx].net_id, 8);
212 memcpy(&in[8], res->data.priv.rand, 8);
213 err = bt_mesh_encrypt(&sub->keys[idx].identity, in, out);
214 if (err) {
215 LOG_ERR("Failed to generate hash (err: %d)", err);
216 return false;
217 }
218
219 if (memcmp(&out[8], res->data.priv.hash, 8)) {
220 return false;
221 }
222
223 return true;
224 }
225
has_net_id(struct bt_mesh_subnet * sub,void * user_data)226 static bool has_net_id(struct bt_mesh_subnet *sub, void *user_data)
227 {
228 struct find_net_id *res = user_data;
229 uint8_t *net_id = NULL;
230
231 if (res->type == BT_MESH_ID_TYPE_NET) {
232 net_id = (uint8_t *)res->data.net_id;
233 goto end;
234 }
235
236 /* Additional handling for BT_MESH_ID_TYPE_PRIV_NET msg type */
237 if (!(is_hash_equal(sub, res, 0) ||
238 (bt_mesh_subnet_has_new_key(sub) && is_hash_equal(sub, res, 1)))) {
239 return false;
240 }
241 end:
242 return proxy_srv_check_and_get(sub, net_id, &res->srv);
243 }
244
handle_net_id(uint8_t type,const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)245 static void handle_net_id(uint8_t type, const struct bt_le_scan_recv_info *info,
246 struct net_buf_simple *buf)
247 {
248 int err;
249 struct find_net_id res;
250 struct bt_mesh_subnet *sub;
251
252 res.type = type;
253 res.srv = NULL;
254
255 if (type == BT_MESH_ID_TYPE_NET) {
256 if (buf->len != 8) {
257 return;
258 }
259 res.data.net_id = net_buf_simple_pull_mem(buf, 8);
260
261 } else {
262 if (buf->len != 16) {
263 return;
264 }
265
266 res.data.priv.hash = net_buf_simple_pull_mem(buf, 8);
267 res.data.priv.rand = net_buf_simple_pull_mem(buf, 8);
268 }
269
270 sub = bt_mesh_subnet_find(has_net_id, (void *)&res);
271 if (sub && res.srv) {
272 err = bt_mesh_gatt_cli_connect(info->addr, &proxy, res.srv);
273 if (err) {
274 LOG_DBG("Failed to connect over GATT (err:%d)", err);
275 }
276 }
277 }
278
bt_mesh_proxy_cli_adv_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)279 void bt_mesh_proxy_cli_adv_recv(const struct bt_le_scan_recv_info *info,
280 struct net_buf_simple *buf)
281 {
282 uint8_t type;
283
284 type = net_buf_simple_pull_u8(buf);
285 switch (type) {
286 case BT_MESH_ID_TYPE_NET:
287 /* Fallthrough */
288 case BT_MESH_ID_TYPE_PRIV_NET: {
289 handle_net_id(type, info, buf);
290 break;
291 }
292 case BT_MESH_ID_TYPE_NODE: {
293 /* TODO */
294 break;
295 }
296 case BT_MESH_ID_TYPE_PRIV_NODE: {
297 /* TODO */
298 break;
299 }
300 default:
301 return;
302 }
303 }
304
bt_mesh_proxy_connect(uint16_t net_idx)305 int bt_mesh_proxy_connect(uint16_t net_idx)
306 {
307 struct bt_mesh_proxy_server *srv;
308
309 if (net_idx == BT_MESH_KEY_ANY) {
310 if (allow_all_subnet) {
311 return -EALREADY;
312 }
313
314 allow_all_subnet = true;
315
316 return 0;
317 }
318
319 srv = find_proxy_srv(net_idx, true, true);
320 if (srv) {
321 return -EALREADY;
322 }
323
324 srv = find_proxy_srv(BT_MESH_KEY_UNUSED, false, true);
325 if (!srv) {
326 return -ENOMEM;
327 }
328
329 srv->net_idx = net_idx;
330
331 return 0;
332 }
333
bt_mesh_proxy_disconnect(uint16_t net_idx)334 int bt_mesh_proxy_disconnect(uint16_t net_idx)
335 {
336 int err;
337 struct bt_mesh_proxy_server *srv;
338
339 if (net_idx != BT_MESH_KEY_ANY) {
340 srv = find_proxy_srv(net_idx, true, true);
341 if (!srv) {
342 return -EALREADY;
343 }
344
345 srv->net_idx = BT_MESH_KEY_UNUSED;
346
347 if (!srv->role) {
348 return 0;
349 }
350
351 return bt_conn_disconnect(srv->role->conn,
352 BT_HCI_ERR_REMOTE_USER_TERM_CONN);
353 }
354
355 if (!allow_all_subnet) {
356 return -EALREADY;
357 }
358
359 allow_all_subnet = false;
360
361 for (int i = 0; i < ARRAY_SIZE(servers); i++) {
362 servers[i].net_idx = BT_MESH_KEY_UNUSED;
363
364 if (!servers[i].role) {
365 continue;
366 }
367
368 err = bt_conn_disconnect(servers[i].role->conn,
369 BT_HCI_ERR_REMOTE_USER_TERM_CONN);
370 if (err) {
371 return err;
372 }
373 }
374
375 return 0;
376 }
377
subnet_evt(struct bt_mesh_subnet * sub,enum bt_mesh_key_evt evt)378 static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
379 {
380 switch (evt) {
381 case BT_MESH_KEY_DELETED:
382 (void)bt_mesh_proxy_disconnect(sub->net_idx);
383 break;
384
385 default:
386 break;
387 }
388 }
389
390 BT_MESH_SUBNET_CB_DEFINE(proxy_cli) = {
391 .evt_handler = subnet_evt,
392 };
393
bt_mesh_proxy_cli_is_connected(uint16_t net_idx)394 bool bt_mesh_proxy_cli_is_connected(uint16_t net_idx)
395 {
396 if (find_proxy_srv(net_idx, true, false)) {
397 return true;
398 }
399
400 return false;
401 }
402