l2cap-tester: Add server tests for Ext-Flowctl
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 8 Mar 2023 23:30:46 +0000 (15:30 -0800)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 5 Jan 2024 10:21:48 +0000 (15:51 +0530)
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

index 3f04640..141dd57 100755 (executable)
@@ -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,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();
 }