From e8fb8805397d43200107de311b1d77098539d3ea Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 8 Mar 2023 15:30:46 -0800 Subject: [PATCH] l2cap-tester: Add server tests for Ext-Flowctl This adds the following tests: L2CAP Ext-Flowctl Server - Success L2CAP Ext-Flowctl Server - Nval SCID L2CAP LE EATT Client - Success L2CAP LE EATT Server - Success L2CAP LE EATT Server - Reject --- tools/l2cap-tester.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 228 insertions(+), 24 deletions(-) diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c index 3f04640..141dd57 100755 --- a/tools/l2cap-tester.c +++ b/tools/l2cap-tester.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -81,6 +82,7 @@ struct l2cap_data { bool server_not_advertising; bool direct_advertising; bool close_1; + bool defer; bool shut_sock_wr; }; @@ -540,6 +542,64 @@ static const struct l2cap_data le_server_nval_scid_test = { .expect_cmd_len = sizeof(nval_le_connect_rsp), }; +static const uint8_t ecred_connect_req[] = { 0x80, 0x00, /* PSM */ + 0x40, 0x00, /* MTU */ + 0x40, 0x00, /* MPS */ + 0x05, 0x00, /* Credits */ + 0x41, 0x00, /* SCID #1 */ + 0x42, 0x00, /* SCID #2 */ + 0x43, 0x00, /* SCID #3 */ + 0x44, 0x00, /* SCID #4 */ + 0x45, 0x00, /* SCID #5 */ +}; + +static const uint8_t ecred_connect_rsp[] = { 0xa0, 0x02, /* MTU */ + 0xbc, 0x00, /* MPS */ + 0x04, 0x00, /* Credits */ + 0x00, 0x00, /* Result */ + 0x40, 0x00, /* DCID #1 */ + 0x41, 0x00, /* DCID #2 */ + 0x42, 0x00, /* DCID #3 */ + 0x43, 0x00, /* DCID #4 */ + 0x44, 0x00, /* DCID #5 */ +}; + +static const struct l2cap_data ext_flowctl_server_success_test = { + .server_psm = 0x0080, + .send_cmd_code = BT_L2CAP_PDU_ECRED_CONN_REQ, + .send_cmd = ecred_connect_req, + .send_cmd_len = sizeof(ecred_connect_req), + .expect_cmd_code = BT_L2CAP_PDU_ECRED_CONN_RSP, + .expect_cmd = ecred_connect_rsp, + .expect_cmd_len = sizeof(ecred_connect_rsp), +}; + +static const uint8_t nval_ecred_connect_req[] = { + 0x80, 0x00, /* PSM */ + 0x40, 0x00, /* MTU */ + 0x40, 0x00, /* MPS */ + 0x05, 0x00, /* Credits */ + 0x01, 0x00, /* SCID #1 */ +}; + +static const uint8_t nval_ecred_connect_rsp[] = { + 0x00, 0x00, /* MTU */ + 0x00, 0x00, /* MPS */ + 0x00, 0x00, /* Credits */ + 0x09, 0x00, /* Result */ + 0x00, 0x00, /* DCID #1 */ +}; + +static const struct l2cap_data ext_flowctl_server_nval_scid_test = { + .server_psm = 0x0080, + .send_cmd_code = BT_L2CAP_PDU_ECRED_CONN_REQ, + .send_cmd = nval_ecred_connect_req, + .send_cmd_len = sizeof(nval_ecred_connect_req), + .expect_cmd_code = BT_L2CAP_PDU_ECRED_CONN_RSP, + .expect_cmd = nval_ecred_connect_rsp, + .expect_cmd_len = sizeof(nval_ecred_connect_rsp), +}; + static const struct l2cap_data le_att_client_connect_success_test_1 = { .cid = 0x0004, .sec_level = BT_SECURITY_LOW, @@ -549,6 +609,69 @@ static const struct l2cap_data le_att_server_success_test_1 = { .cid = 0x0004, }; +static const struct l2cap_data le_eatt_client_connect_success_test_1 = { + .client_psm = 0x0027, + .server_psm = 0x0027, + .mode = BT_MODE_EXT_FLOWCTL, + .sec_level = BT_SECURITY_LOW, +}; + +static const uint8_t eatt_connect_req[] = { 0x27, 0x00, /* PSM */ + 0x40, 0x00, /* MTU */ + 0x40, 0x00, /* MPS */ + 0x05, 0x00, /* Credits */ + 0x41, 0x00, /* SCID #1 */ +}; + +static const uint8_t eatt_connect_rsp[] = { 0xa0, 0x02, /* MTU */ + 0xbc, 0x00, /* MPS */ + 0x04, 0x00, /* Credits */ + 0x00, 0x00, /* Result */ + 0x40, 0x00, /* DCID #1 */ +}; + +static const struct l2cap_data le_eatt_server_success_test_1 = { + .server_psm = 0x0027, + .mode = BT_MODE_EXT_FLOWCTL, + .send_cmd_code = BT_L2CAP_PDU_ECRED_CONN_REQ, + .send_cmd = eatt_connect_req, + .send_cmd_len = sizeof(eatt_connect_req), + .expect_cmd_code = BT_L2CAP_PDU_ECRED_CONN_RSP, + .expect_cmd = eatt_connect_rsp, + .expect_cmd_len = sizeof(eatt_connect_rsp), + .defer = true, +}; + +static const uint8_t eatt_reject_req[] = { 0x27, 0x00, /* PSM */ + 0x40, 0x00, /* MTU */ + 0x40, 0x00, /* MPS */ + 0x05, 0x00, /* Credits */ + 0x41, 0x00, /* SCID #1 */ + 0x42, 0x00, /* SCID #2 */ + 0x43, 0x00, /* SCID #3 */ + 0x44, 0x00, /* SCID #4 */ + 0x45, 0x00, /* SCID #5 */ +}; + +static const uint8_t eatt_reject_rsp[] = { 0xa0, 0x02, /* MTU */ + 0xbc, 0x00, /* MPS */ + 0x04, 0x00, /* Credits */ + 0x06, 0x00, /* Result */ +}; + +static const struct l2cap_data le_eatt_server_reject_test_1 = { + .server_psm = 0x0027, + .mode = BT_MODE_EXT_FLOWCTL, + .send_cmd_code = BT_L2CAP_PDU_ECRED_CONN_REQ, + .send_cmd = eatt_reject_req, + .send_cmd_len = sizeof(eatt_reject_req), + .expect_cmd_code = BT_L2CAP_PDU_ECRED_CONN_RSP, + .expect_cmd = eatt_reject_rsp, + .expect_cmd_len = sizeof(eatt_reject_rsp), + .defer = true, + .expect_err = -1, +}; + static const struct l2cap_data ext_flowctl_client_connect_success_test_1 = { .client_psm = 0x0080, .server_psm = 0x0080, @@ -1689,43 +1812,29 @@ static void test_connect_2(const void *test_data) defer); } -static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond, +static gboolean l2cap_accept_cb(GIOChannel *io, GIOCondition cond, gpointer user_data) { struct test_data *data = tester_get_data(); const struct l2cap_data *l2data = data->test_data; - int sk, new_sk; - - data->io_id = 0; + int sk; sk = g_io_channel_unix_get_fd(io); - new_sk = accept(sk, NULL, NULL); - if (new_sk < 0) { - tester_warn("accept failed: %s (%u)", strerror(errno), errno); - tester_test_failed(); - return FALSE; - } - - if (!check_mtu(data, new_sk)) { + if (!check_mtu(data, sk)) { tester_test_failed(); - close(new_sk); return FALSE; } if (l2data->read_data) { struct bthost *bthost; - GIOChannel *new_io; - - new_io = g_io_channel_unix_new(new_sk); - g_io_channel_set_close_on_unref(new_io, TRUE); bthost = hciemu_client_get_host(data->hciemu); - g_io_add_watch(new_io, G_IO_IN, server_received_data, NULL); + g_io_add_watch(io, G_IO_IN, server_received_data, NULL); bthost_send_cid(bthost, data->handle, data->dcid, l2data->read_data, l2data->data_len); - g_io_channel_unref(new_io); + g_io_channel_unref(io); return FALSE; } else if (l2data->write_data) { @@ -1736,8 +1845,7 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond, bthost_add_cid_hook(bthost, data->handle, data->scid, server_bthost_received_data, NULL); - ret = write(new_sk, l2data->write_data, l2data->data_len); - close(new_sk); + ret = write(sk, l2data->write_data, l2data->data_len); if (ret != l2data->data_len) { tester_warn("Unable to write all data"); @@ -1749,13 +1857,81 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond, tester_print("Successfully connected"); - close(new_sk); - tester_test_passed(); return FALSE; } +static bool defer_accept(struct test_data *data, GIOChannel *io) +{ + int sk; + char c; + struct pollfd pfd; + + sk = g_io_channel_unix_get_fd(io); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sk; + pfd.events = POLLOUT; + + if (poll(&pfd, 1, 0) < 0) { + tester_warn("poll: %s (%d)", strerror(errno), errno); + return false; + } + + if (!(pfd.revents & POLLOUT)) { + if (read(sk, &c, 1) < 0) { + tester_warn("read: %s (%d)", strerror(errno), errno); + return false; + } + } + + data->io_id = g_io_add_watch(io, G_IO_OUT, l2cap_accept_cb, NULL); + + g_io_channel_unref(io); + + tester_print("Accept deferred setup"); + + return true; +} + +static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct test_data *data = tester_get_data(); + const struct l2cap_data *l2data = data->test_data; + int sk, new_sk; + + data->io_id = 0; + + sk = g_io_channel_unix_get_fd(io); + + new_sk = accept(sk, NULL, NULL); + if (new_sk < 0) { + tester_warn("accept failed: %s (%u)", strerror(errno), errno); + tester_test_failed(); + return FALSE; + } + + io = g_io_channel_unix_new(new_sk); + g_io_channel_set_close_on_unref(io, TRUE); + + if (l2data->defer) { + if (l2data->expect_err < 0) { + g_io_channel_unref(io); + return FALSE; + } + + if (!defer_accept(data, io)) { + tester_warn("Unable to accept deferred setup"); + tester_test_failed(); + } + return FALSE; + } + + return l2cap_accept_cb(io, cond, user_data); +} + static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len, void *user_data) { @@ -1767,7 +1943,7 @@ static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len, if (code != l2data->expect_cmd_code) { tester_warn("Unexpected L2CAP response code (expected 0x%02x)", l2data->expect_cmd_code); - return; + goto failed; } if (code == BT_L2CAP_PDU_CONN_RSP) { @@ -1844,6 +2020,8 @@ static void test_server(const void *test_data) int sk; if (l2data->server_psm || l2data->cid) { + int opt = 1; + sk = create_l2cap_sock(data, l2data->server_psm, l2data->cid, l2data->sec_level, l2data->mode); @@ -1852,6 +2030,15 @@ static void test_server(const void *test_data) return; } + if (l2data->defer && setsockopt(sk, SOL_BLUETOOTH, + BT_DEFER_SETUP, &opt, sizeof(opt)) < 0) { + tester_warn("Can't enable deferred setup: %s (%d)", + strerror(errno), errno); + tester_test_failed(); + close(sk); + return; + } + if (listen(sk, 5) < 0) { tester_warn("listening on socket failed: %s (%u)", strerror(errno), errno); @@ -2066,6 +2253,13 @@ int main(int argc, char *argv[]) setup_powered_client, test_connect_2); + test_l2cap_le("L2CAP Ext-Flowctl Server - Success", + &ext_flowctl_server_success_test, + setup_powered_server, test_server); + test_l2cap_le("L2CAP Ext-Flowctl Server - Nval SCID", + &ext_flowctl_server_nval_scid_test, + setup_powered_server, test_server); + test_l2cap_le("L2CAP LE ATT Client - Success", &le_att_client_connect_success_test_1, setup_powered_client, test_connect); @@ -2073,5 +2267,15 @@ int main(int argc, char *argv[]) &le_att_server_success_test_1, setup_powered_server, test_server); + test_l2cap_le("L2CAP LE EATT Client - Success", + &le_eatt_client_connect_success_test_1, + setup_powered_client, test_connect); + test_l2cap_le("L2CAP LE EATT Server - Success", + &le_eatt_server_success_test_1, + setup_powered_server, test_server); + test_l2cap_le("L2CAP LE EATT Server - Reject", + &le_eatt_server_reject_test_1, + setup_powered_server, test_server); + return tester_run(); } -- 2.7.4