1 /* Copyright (c) 2023 Nordic Semiconductor ASA
2  * SPDX-License-Identifier: Apache-2.0
3  */
4 
5 #include <stdint.h>
6 
7 #include <zephyr/kernel.h>
8 
9 #include <zephyr/bluetooth/addr.h>
10 #include <zephyr/bluetooth/conn.h>
11 #include <zephyr/bluetooth/gatt.h>
12 #include <zephyr/bluetooth/uuid.h>
13 #include <zephyr/bluetooth/bluetooth.h>
14 
15 #include <zephyr/logging/log.h>
16 
17 #include <zephyr/settings/settings.h>
18 
19 #include "babblekit/testcase.h"
20 #include "babblekit/flags.h"
21 #include "common.h"
22 #include "settings.h"
23 
24 #include "argparse.h"
25 #include "bs_pc_backchannel.h"
26 
27 #define GOOD_CLIENT_CHAN 0
28 #define BAD_CLIENT_CHAN 1
29 
30 DEFINE_FLAG_STATIC(connected_flag);
31 DEFINE_FLAG_STATIC(disconnected_flag);
32 DEFINE_FLAG_STATIC(security_updated_flag);
33 DEFINE_FLAG_STATIC(ccc_cfg_changed_flag);
34 
35 static const struct bt_uuid_128 dummy_service = BT_UUID_INIT_128(DUMMY_SERVICE_TYPE);
36 
37 static const struct bt_uuid_128 notify_characteristic_uuid =
38 					BT_UUID_INIT_128(DUMMY_SERVICE_NOTIFY_TYPE);
39 
40 static struct bt_conn *default_conn;
41 
42 static struct bt_conn_cb peripheral_cb;
43 
ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)44 static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value)
45 {
46 	ARG_UNUSED(attr);
47 
48 	bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
49 
50 	LOG_INF("CCC Update: notification %s", notif_enabled ? "enabled" : "disabled");
51 
52 	SET_FLAG(ccc_cfg_changed_flag);
53 }
54 
55 BT_GATT_SERVICE_DEFINE(dummy_svc, BT_GATT_PRIMARY_SERVICE(&dummy_service),
56 		       BT_GATT_CHARACTERISTIC(&notify_characteristic_uuid.uuid, BT_GATT_CHRC_NOTIFY,
57 					      BT_GATT_PERM_NONE, NULL, NULL, NULL),
58 		       BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
59 
create_adv(struct bt_le_ext_adv ** adv)60 static void create_adv(struct bt_le_ext_adv **adv)
61 {
62 	int err;
63 	struct bt_le_adv_param params;
64 
65 	memset(&params, 0, sizeof(struct bt_le_adv_param));
66 
67 	params.options |= BT_LE_ADV_OPT_CONN;
68 
69 	params.id = BT_ID_DEFAULT;
70 	params.sid = 0;
71 	params.interval_min = BT_GAP_ADV_FAST_INT_MIN_2;
72 	params.interval_max = BT_GAP_ADV_FAST_INT_MAX_2;
73 
74 	err = bt_le_ext_adv_create(&params, NULL, adv);
75 	if (err) {
76 		TEST_FAIL("Failed to create advertiser (%d)", err);
77 	}
78 }
79 
start_adv(struct bt_le_ext_adv * adv)80 static void start_adv(struct bt_le_ext_adv *adv)
81 {
82 	int err;
83 	int32_t timeout = 0;
84 	int8_t num_events = 0;
85 
86 	struct bt_le_ext_adv_start_param start_params;
87 
88 	start_params.timeout = timeout;
89 	start_params.num_events = num_events;
90 
91 	err = bt_le_ext_adv_start(adv, &start_params);
92 	if (err) {
93 		TEST_FAIL("Failed to start advertiser (err %d)", err);
94 	}
95 
96 	LOG_DBG("Advertiser started");
97 }
98 
stop_adv(struct bt_le_ext_adv * adv)99 static void stop_adv(struct bt_le_ext_adv *adv)
100 {
101 	int err;
102 
103 	err = bt_le_ext_adv_stop(adv);
104 	if (err) {
105 		TEST_FAIL("Failed to stop advertiser (err %d)", err);
106 	}
107 }
108 
connected(struct bt_conn * conn,uint8_t err)109 static void connected(struct bt_conn *conn, uint8_t err)
110 {
111 	char addr_str[BT_ADDR_LE_STR_LEN];
112 
113 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
114 
115 	if (err) {
116 		TEST_FAIL("Failed to connect to %s (err %d)", addr_str, err);
117 	}
118 
119 	LOG_DBG("Connected: %s", addr_str);
120 
121 	default_conn = bt_conn_ref(conn);
122 
123 	SET_FLAG(connected_flag);
124 	UNSET_FLAG(disconnected_flag);
125 }
126 
disconnected(struct bt_conn * conn,uint8_t reason)127 static void disconnected(struct bt_conn *conn, uint8_t reason)
128 {
129 	char addr_str[BT_ADDR_LE_STR_LEN];
130 
131 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
132 
133 	LOG_DBG("Disconnected: %s (reason 0x%02x)", addr_str, reason);
134 
135 	bt_conn_unref(conn);
136 	default_conn = NULL;
137 
138 	SET_FLAG(disconnected_flag);
139 	UNSET_FLAG(connected_flag);
140 }
141 
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)142 static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
143 {
144 	char addr_str[BT_ADDR_LE_STR_LEN];
145 
146 	bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
147 
148 	if (!err) {
149 		LOG_DBG("Security changed: %s level %u", addr_str, level);
150 		SET_FLAG(security_updated_flag);
151 	} else {
152 		LOG_DBG("Security failed: %s level %u err %d", addr_str, level, err);
153 	}
154 }
155 
is_peer_subscribed(struct bt_conn * conn)156 static bool is_peer_subscribed(struct bt_conn *conn)
157 {
158 	struct bt_gatt_attr *attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_DUMMY_SERVICE_NOTIFY);
159 	bool is_subscribed = bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY);
160 
161 	return is_subscribed;
162 }
163 
164 /* Test steps */
165 
connect_pair_check_subscribtion(struct bt_le_ext_adv * adv)166 static void connect_pair_check_subscribtion(struct bt_le_ext_adv *adv)
167 {
168 	start_adv(adv);
169 
170 	WAIT_FOR_FLAG(connected_flag);
171 
172 	WAIT_FOR_FLAG(security_updated_flag);
173 	UNSET_FLAG(security_updated_flag);
174 
175 	/* wait for confirmation of subscribtion from good client */
176 	backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
177 
178 	/* check that subscribtion request did not fail */
179 	if (!is_peer_subscribed(default_conn)) {
180 		TEST_FAIL("Good client did not subscribed");
181 	}
182 
183 	stop_adv(adv);
184 
185 	/* confirm to good client that the subscribtion has been well registered */
186 	backchannel_sync_send(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
187 }
188 
connect_wait_unsubscribtion(struct bt_le_ext_adv * adv)189 static void connect_wait_unsubscribtion(struct bt_le_ext_adv *adv)
190 {
191 	UNSET_FLAG(ccc_cfg_changed_flag);
192 
193 	start_adv(adv);
194 
195 	WAIT_FOR_FLAG(connected_flag);
196 
197 	stop_adv(adv);
198 
199 	/* check that subscribtion is restored for bad client */
200 	if (!is_peer_subscribed(default_conn)) {
201 		TEST_FAIL("Subscribtion has not been restored for bad client");
202 	}
203 
204 	/* confirm to bad client that the subscribtion had not been restored */
205 	backchannel_sync_send(BAD_CLIENT_CHAN, BAD_CLIENT_ID);
206 	/* wait for confirmation that bad client requested unsubscribtion */
207 	backchannel_sync_wait(BAD_CLIENT_CHAN, BAD_CLIENT_ID);
208 
209 	/* check that unsubscribtion request didn't fail */
210 	if (!IS_FLAG_SET(ccc_cfg_changed_flag)) {
211 		TEST_FAIL("Bad client didn't manage to update CCC config");
212 	}
213 
214 	/* confirm to bad client that unsubscribtion request has been well registered */
215 	backchannel_sync_send(BAD_CLIENT_CHAN, BAD_CLIENT_ID);
216 }
217 
connect_restore_sec_check_subscribtion(struct bt_le_ext_adv * adv)218 static void connect_restore_sec_check_subscribtion(struct bt_le_ext_adv *adv)
219 {
220 	start_adv(adv);
221 
222 	WAIT_FOR_FLAG(connected_flag);
223 
224 	WAIT_FOR_FLAG(security_updated_flag);
225 	UNSET_FLAG(security_updated_flag);
226 
227 	/* wait for good client end of security update */
228 	backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
229 
230 	/* check that subscribtion hasn't been restored */
231 	if (is_peer_subscribed(default_conn)) {
232 		TEST_FAIL("Good client is subscribed");
233 	}
234 
235 	/* confirm to good client that the subscribtion has been well restored */
236 	backchannel_sync_send(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
237 	/* wait for confimation of unsubscribtion from good client */
238 	backchannel_sync_wait(GOOD_CLIENT_CHAN, GOOD_CLIENT_ID);
239 
240 	/* check that unsubscribtion request from good client has been registered */
241 	if (is_peer_subscribed(default_conn)) {
242 		TEST_FAIL("Good client did not unsubscribe");
243 	}
244 }
245 
246 /* Util functions */
247 
peripheral_backchannel_init(void)248 void peripheral_backchannel_init(void)
249 {
250 	uint device_number = get_device_nbr();
251 	uint channel_numbers[2] = {
252 		0,
253 		0,
254 	};
255 	uint device_numbers[2] = {
256 		GOOD_CLIENT_ID,
257 		BAD_CLIENT_ID,
258 	};
259 	uint num_ch = 2;
260 	uint *ch;
261 
262 	LOG_DBG("Opening back channels for device %d", device_number);
263 	ch = bs_open_back_channel(device_number, device_numbers, channel_numbers, num_ch);
264 	if (!ch) {
265 		TEST_FAIL("Unable to open backchannel");
266 	}
267 }
268 
check_ccc_handle(void)269 static void check_ccc_handle(void)
270 {
271 	struct bt_gatt_attr *service_notify_attr =
272 		bt_gatt_find_by_uuid(NULL, 0, &notify_characteristic_uuid.uuid);
273 
274 	struct bt_gatt_attr *ccc_attr =
275 		bt_gatt_find_by_uuid(service_notify_attr, 0, BT_UUID_GATT_CCC);
276 	uint16_t actual_ccc_handle = bt_gatt_attr_get_handle(ccc_attr);
277 
278 	__ASSERT(actual_ccc_handle == CCC_HANDLE,
279 		 "Please update the CCC_HANDLE define (actual_ccc_handle=%d)", actual_ccc_handle);
280 }
281 
282 /* Main function */
283 
run_peripheral(void)284 void run_peripheral(void)
285 {
286 	/*
287 	 * test goal: demonstrate the expected behavior of the GATT server when
288 	 * a non-bonded peer try to unsubscribe from a previously subscription
289 	 * done in a bonded context
290 	 *
291 	 * test pass if the bad client manage to unsubscribe
292 	 */
293 
294 	int err;
295 	struct bt_le_ext_adv *adv = NULL;
296 
297 	peripheral_cb.connected = connected;
298 	peripheral_cb.disconnected = disconnected;
299 	peripheral_cb.security_changed = security_changed;
300 
301 	peripheral_backchannel_init();
302 
303 	err = bt_enable(NULL);
304 	if (err) {
305 		TEST_FAIL("Bluetooth init failed (err %d)", err);
306 	}
307 
308 	LOG_DBG("Bluetooth initialized");
309 
310 	check_ccc_handle();
311 
312 	bt_conn_cb_register(&peripheral_cb);
313 
314 	err = settings_load();
315 	if (err) {
316 		TEST_FAIL("Settings load failed (err %d)", err);
317 	}
318 
319 	err = bt_unpair(BT_ID_DEFAULT, BT_ADDR_LE_ANY);
320 	if (err) {
321 		TEST_FAIL("Unpairing failed (err %d)", err);
322 	}
323 
324 	create_adv(&adv);
325 
326 	connect_pair_check_subscribtion(adv);
327 	WAIT_FOR_FLAG(disconnected_flag);
328 
329 	connect_wait_unsubscribtion(adv);
330 	WAIT_FOR_FLAG(disconnected_flag);
331 
332 	connect_restore_sec_check_subscribtion(adv);
333 	WAIT_FOR_FLAG(disconnected_flag);
334 
335 	TEST_PASS("Peripheral test passed");
336 }
337