2023-03-10 00:00:56

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ] l2cap-tester: Add server tests for Ext-Flowctl

From: Luiz Augusto von Dentz <[email protected]>

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 | 287 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 246 insertions(+), 41 deletions(-)

diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c
index 3f04640131fa..922533f34133 100644
--- a/tools/l2cap-tester.c
+++ b/tools/l2cap-tester.c
@@ -15,6 +15,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
+#include <poll.h>
#include <stdbool.h>

#include <glib.h>
@@ -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,6 +1812,89 @@ static void test_connect_2(const void *test_data)
defer);
}

+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;
+
+ sk = g_io_channel_unix_get_fd(io);
+
+ if (!check_mtu(data, sk)) {
+ tester_test_failed();
+ return FALSE;
+ }
+
+ if (l2data->read_data) {
+ struct bthost *bthost;
+
+ bthost = hciemu_client_get_host(data->hciemu);
+ 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(io);
+
+ return FALSE;
+ } else if (l2data->write_data) {
+ struct bthost *bthost;
+ ssize_t ret;
+
+ bthost = hciemu_client_get_host(data->hciemu);
+ bthost_add_cid_hook(bthost, data->handle, data->scid,
+ server_bthost_received_data, NULL);
+
+ ret = write(sk, l2data->write_data, l2data->data_len);
+
+ if (ret != l2data->data_len) {
+ tester_warn("Unable to write all data");
+ tester_test_failed();
+ }
+
+ return FALSE;
+ }
+
+ tester_print("Successfully connected");
+
+ 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)
{
@@ -1707,53 +1913,24 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,
return FALSE;
}

- if (!check_mtu(data, new_sk)) {
- tester_test_failed();
- close(new_sk);
- return FALSE;
- }
+ io = g_io_channel_unix_new(new_sk);
+ g_io_channel_set_close_on_unref(io, TRUE);

- 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);
- bthost_send_cid(bthost, data->handle, data->dcid,
- l2data->read_data, l2data->data_len);
-
- g_io_channel_unref(new_io);
-
- return FALSE;
- } else if (l2data->write_data) {
- struct bthost *bthost;
- ssize_t ret;
-
- bthost = hciemu_client_get_host(data->hciemu);
- 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);
-
- if (ret != l2data->data_len) {
- tester_warn("Unable to write all data");
- tester_test_failed();
+ if (l2data->defer) {
+ if (l2data->expect_err < 0) {
+ g_io_channel_unref(io);
+ tester_test_passed();
+ return FALSE;
}

+ if (!defer_accept(data, io)) {
+ tester_warn("Unable to accept deferred setup");
+ tester_test_failed();
+ }
return FALSE;
}

- tester_print("Successfully connected");
-
- close(new_sk);
-
- tester_test_passed();
-
- return FALSE;
+ return l2cap_accept_cb(io, cond, user_data);
}

static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len,
@@ -1844,6 +2021,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 +2031,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 +2254,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 +2268,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.39.2