blob: c6b73a933bfa86e32736bd5172a5d44036116763 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <stddef.h>
#include <errno.h>
#include <string.h>
#include "testutil/testutil.h"
#include "host/ble_hs_test.h"
#include "ble_hs_test_util.h"
#define BLE_HS_PVCY_TEST_MAX_GAP_EVENTS 256
static struct ble_gap_event
ble_hs_pvcy_test_gap_events[BLE_HS_PVCY_TEST_MAX_GAP_EVENTS];
static int ble_hs_pvcy_test_num_gap_events;
static void
ble_hs_pvcy_test_util_init(void)
{
ble_hs_test_util_init();
ble_hs_pvcy_test_num_gap_events = 0;
}
static int
ble_hs_pvcy_test_util_gap_event(struct ble_gap_event *event, void *arg)
{
TEST_ASSERT_FATAL(ble_hs_pvcy_test_num_gap_events <
BLE_HS_PVCY_TEST_MAX_GAP_EVENTS);
ble_hs_pvcy_test_gap_events[ble_hs_pvcy_test_num_gap_events++] = *event;
return 0;
}
static void
ble_hs_pvcy_test_util_all_gap_procs(int adv_status,
int conn_status,
int disc_status)
{
struct ble_gap_disc_params disc_params;
ble_addr_t peer_addr;
int rc;
/* Advertise. */
rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
NULL, &ble_hs_test_util_adv_params,
BLE_HS_FOREVER,
ble_hs_pvcy_test_util_gap_event,
NULL, 0, 0);
TEST_ASSERT_FATAL(rc == adv_status);
if (rc == 0) {
rc = ble_hs_test_util_adv_stop(0);
TEST_ASSERT_FATAL(rc == 0);
}
/* Connect. */
peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} };
rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr,
BLE_HS_FOREVER, NULL,
ble_hs_pvcy_test_util_gap_event, NULL, 0);
TEST_ASSERT_FATAL(rc == conn_status);
if (rc == 0) {
ble_hs_test_util_conn_cancel_full();
}
/* Discover. */
disc_params = (struct ble_gap_disc_params){ 0 };
rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
&disc_params, ble_hs_pvcy_test_util_gap_event,
NULL, -1, 0);
TEST_ASSERT_FATAL(rc == disc_status);
if (rc == 0) {
rc = ble_hs_test_util_disc_cancel(0);
TEST_ASSERT_FATAL(rc == 0);
}
}
static void
ble_hs_pvcy_test_util_add_irk_set_acks(void)
{
ble_hs_test_util_hci_ack_append(
BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_ADD_RESOLV_LIST), 0);
ble_hs_test_util_hci_ack_append(
BLE_HCI_OP(BLE_HCI_OGF_LE, BLE_HCI_OCF_LE_SET_PRIVACY_MODE), 0);
}
static void
ble_hs_pvcy_test_util_start_host(int num_expected_irks)
{
int rc;
int i;
/* Clear our IRK. This ensures the full startup sequence, including
* setting the default IRK, takes place. We need this so that we can plan
* which HCI acks to fake.
*/
rc = ble_hs_test_util_set_our_irk((uint8_t[16]){0}, -1, 0);
TEST_ASSERT_FATAL(rc == 0);
ble_hs_test_util_hci_out_clear();
ble_hs_test_util_hci_ack_set_startup();
for (i = 0; i < num_expected_irks; i++) {
ble_hs_pvcy_test_util_add_irk_set_acks();
}
rc = ble_hs_start();
TEST_ASSERT_FATAL(rc == 0);
/* Discard startup HCI commands. */
ble_hs_test_util_hci_out_adj(ble_hs_test_util_hci_startup_seq_cnt());
}
static void
ble_hs_pvcy_test_util_add_irk_verify_tx(const ble_addr_t *peer_addr,
const uint8_t *peer_irk,
const uint8_t *local_irk)
{
ble_hs_test_util_hci_verify_tx_add_irk(peer_addr->type,
peer_addr->val,
peer_irk,
local_irk);
ble_hs_test_util_hci_verify_tx_set_priv_mode(peer_addr->type,
peer_addr->val,
BLE_GAP_PRIVATE_MODE_DEVICE);
}
static void
ble_hs_pvcy_test_util_add_irk(const ble_addr_t *peer_addr,
const uint8_t *peer_irk,
const uint8_t *local_irk)
{
int rc;
ble_hs_pvcy_test_util_add_irk_set_acks();
rc = ble_hs_pvcy_add_entry(peer_addr->val, peer_addr->type, peer_irk);
TEST_ASSERT_FATAL(rc == 0);
ble_hs_test_util_hci_out_adj(-2);
ble_hs_pvcy_test_util_add_irk_verify_tx(peer_addr, peer_irk, local_irk);
}
static void
ble_hs_pvcy_test_util_add_arbitrary_irk(void)
{
ble_addr_t peer_addr;
peer_addr = (ble_addr_t) {
.type = BLE_ADDR_PUBLIC,
.val = {1,2,3,4,5,6},
};
ble_hs_pvcy_test_util_add_irk(
&peer_addr,
(uint8_t[16]){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16},
ble_hs_pvcy_default_irk);
}
static void
ble_hs_pvcy_test_util_restore_irk(const struct ble_store_value_sec *value_sec)
{
int rc;
ble_hs_pvcy_test_util_add_irk_set_acks();
rc = ble_store_write_peer_sec(value_sec);
TEST_ASSERT_FATAL(rc == 0);
ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec->peer_addr,
value_sec->irk,
ble_hs_pvcy_default_irk);
}
TEST_CASE(ble_hs_pvcy_test_case_restore_irks)
{
struct ble_store_value_sec value_sec1;
struct ble_store_value_sec value_sec2;
ble_hs_pvcy_test_util_init();
/*** No persisted IRKs. */
ble_hs_pvcy_test_util_start_host(0);
/*** One persisted IRK. */
/* Persist IRK; ensure it automatically gets added to the list. */
value_sec1 = (struct ble_store_value_sec) {
.peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } },
.key_size = 16,
.ediv = 1,
.rand_num = 2,
.irk = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 },
.irk_present = 1,
};
ble_hs_pvcy_test_util_restore_irk(&value_sec1);
/* Ensure it gets added to list on startup. */
ble_hs_pvcy_test_util_start_host(1);
ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec1.peer_addr,
value_sec1.irk,
ble_hs_pvcy_default_irk);
/* Two persisted IRKs. */
value_sec2 = (struct ble_store_value_sec) {
.peer_addr = { BLE_ADDR_PUBLIC, { 2, 3, 4, 5, 6, 7 } },
.key_size = 16,
.ediv = 12,
.rand_num = 20,
.irk = { 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 9, 9, 9, 9, 9, 10 },
.irk_present = 1,
};
ble_hs_pvcy_test_util_restore_irk(&value_sec2);
/* Ensure both get added to list on startup. */
ble_hs_pvcy_test_util_start_host(2);
ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec1.peer_addr,
value_sec1.irk,
ble_hs_pvcy_default_irk);
ble_hs_pvcy_test_util_add_irk_verify_tx(&value_sec2.peer_addr,
value_sec2.irk,
ble_hs_pvcy_default_irk);
}
/** No active GAP procedures. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_idle)
{
ble_hs_pvcy_test_util_init();
ble_hs_pvcy_test_util_add_arbitrary_irk();
TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 0);
}
/*** Advertising active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv)
{
int rc;
ble_hs_pvcy_test_util_init();
/* Start an advertising procedure. */
rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
NULL, &ble_hs_test_util_adv_params,
BLE_HS_FOREVER,
ble_hs_pvcy_test_util_gap_event,
NULL, 0, 0);
TEST_ASSERT_FATAL(rc == 0);
ble_hs_test_util_hci_ack_set(
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE),
0);
ble_hs_pvcy_test_util_add_arbitrary_irk();
TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
BLE_GAP_EVENT_ADV_COMPLETE);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason ==
BLE_HS_EPREEMPTED);
/* Ensure GAP procedures are no longer preempted. */
ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
}
/*** Discovery active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_disc)
{
struct ble_gap_disc_params disc_params;
int rc;
ble_hs_pvcy_test_util_init();
/* Start an advertising procedure. */
disc_params = (struct ble_gap_disc_params){ 0 };
rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
&disc_params, ble_hs_pvcy_test_util_gap_event,
NULL, -1, 0);
TEST_ASSERT_FATAL(rc == 0);
ble_hs_test_util_hci_ack_set(
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_ENABLE),
0);
ble_hs_pvcy_test_util_add_arbitrary_irk();
TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
BLE_GAP_EVENT_DISC_COMPLETE);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].disc_complete.reason ==
BLE_HS_EPREEMPTED);
/* Ensure GAP procedures are no longer preempted. */
ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
}
/*** Connect active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_conn)
{
ble_addr_t peer_addr;
int rc;
ble_hs_pvcy_test_util_init();
/* Start a connect procedure. */
peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} };
rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr,
BLE_HS_FOREVER, NULL,
ble_hs_pvcy_test_util_gap_event, NULL, 0);
TEST_ASSERT_FATAL(rc == 0);
ble_hs_test_util_hci_ack_set(
BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CREATE_CONN_CANCEL),
0);
ble_hs_pvcy_test_util_add_arbitrary_irk();
/* Cancel is now in progress. */
TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 0);
/* Ensure no GAP procedures are allowed. */
ble_hs_pvcy_test_util_all_gap_procs(BLE_HS_EPREEMPTED,
BLE_HS_EALREADY,
BLE_HS_EBUSY);
/* Receive cancel event. */
ble_hs_test_util_rx_conn_cancel_evt();
TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
BLE_GAP_EVENT_CONNECT);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].connect.status ==
BLE_HS_EPREEMPTED);
/* Ensure GAP procedures are no longer preempted. */
ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
}
/*** Advertising and discovery active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_disc)
{
struct ble_gap_disc_params disc_params;
int rc;
ble_hs_pvcy_test_util_init();
/* Start an advertising procedure. */
rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
NULL, &ble_hs_test_util_adv_params,
BLE_HS_FOREVER,
ble_hs_pvcy_test_util_gap_event,
NULL, 0, 0);
TEST_ASSERT_FATAL(rc == 0);
/* Start a discovery procedure. */
disc_params = (struct ble_gap_disc_params){ 0 };
rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, BLE_HS_FOREVER,
&disc_params, ble_hs_pvcy_test_util_gap_event,
NULL, -1, 0);
TEST_ASSERT_FATAL(rc == 0);
ble_hs_test_util_hci_ack_set_seq((struct ble_hs_test_util_hci_ack[]) {
{ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE), 0 },
{ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_SCAN_ENABLE), 0 },
{ 0 },
});
ble_hs_pvcy_test_util_add_arbitrary_irk();
TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 2);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
BLE_GAP_EVENT_ADV_COMPLETE);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason ==
BLE_HS_EPREEMPTED);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].type ==
BLE_GAP_EVENT_DISC_COMPLETE);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].disc_complete.reason ==
BLE_HS_EPREEMPTED);
/* Ensure GAP procedures are no longer preempted. */
ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
}
/*** Advertising and connecting active. */
TEST_CASE(ble_hs_pvcy_test_case_add_irk_adv_conn)
{
ble_addr_t peer_addr;
int rc;
ble_hs_pvcy_test_util_init();
/* Start an advertising procedure. */
rc = ble_hs_test_util_adv_start(BLE_OWN_ADDR_PUBLIC,
NULL, &ble_hs_test_util_adv_params,
BLE_HS_FOREVER,
ble_hs_pvcy_test_util_gap_event,
NULL, 0, 0);
TEST_ASSERT_FATAL(rc == 0);
/* Start a connect procedure. */
peer_addr = (ble_addr_t){ BLE_ADDR_PUBLIC, {1,2,3,4,5,6} };
rc = ble_hs_test_util_connect(BLE_ADDR_PUBLIC, &peer_addr,
BLE_HS_FOREVER, NULL,
ble_hs_pvcy_test_util_gap_event, NULL, 0);
TEST_ASSERT_FATAL(rc == 0);
ble_hs_test_util_hci_ack_set_seq((struct ble_hs_test_util_hci_ack[]) {
{ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_SET_ADV_ENABLE), 0 },
{ BLE_HS_TEST_UTIL_LE_OPCODE(BLE_HCI_OCF_LE_CREATE_CONN_CANCEL), 0 },
{ 0 },
});
ble_hs_pvcy_test_util_add_arbitrary_irk();
/* Cancel is now in progress. */
TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 1);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].type ==
BLE_GAP_EVENT_ADV_COMPLETE);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[0].adv_complete.reason ==
BLE_HS_EPREEMPTED);
/* Ensure no GAP procedures are allowed. */
ble_hs_pvcy_test_util_all_gap_procs(BLE_HS_EPREEMPTED,
BLE_HS_EALREADY,
BLE_HS_EBUSY);
/* Receive cancel event. */
ble_hs_test_util_rx_conn_cancel_evt();
TEST_ASSERT(ble_hs_pvcy_test_num_gap_events == 2);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].type ==
BLE_GAP_EVENT_CONNECT);
TEST_ASSERT(ble_hs_pvcy_test_gap_events[1].connect.status ==
BLE_HS_EPREEMPTED);
/* Ensure GAP procedures are no longer preempted. */
ble_hs_pvcy_test_util_all_gap_procs(0, 0, 0);
}
TEST_SUITE(ble_hs_pvcy_test_suite_irk)
{
tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL);
ble_hs_pvcy_test_case_restore_irks();
ble_hs_pvcy_test_case_add_irk_idle();
ble_hs_pvcy_test_case_add_irk_adv();
ble_hs_pvcy_test_case_add_irk_disc();
ble_hs_pvcy_test_case_add_irk_conn();
ble_hs_pvcy_test_case_add_irk_adv_disc();
ble_hs_pvcy_test_case_add_irk_adv_conn();
}
int
ble_hs_pvcy_test_all(void)
{
ble_hs_pvcy_test_suite_irk();
return tu_any_failed;
}