All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ 1/4] bthost: Fix not handling ACL fragmentation
@ 2022-02-09 23:55 Luiz Augusto von Dentz
  2022-02-09 23:55 ` [PATCH BlueZ 2/4] bthost: Fix length calculation for RFCOMM header Luiz Augusto von Dentz
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2022-02-09 23:55 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Large packets requires the support of ACL fragmentation in order to be
properly processed.
---
 emulator/bthost.c | 121 ++++++++++++++++++++++++++++++++++++++--------
 monitor/bt.h      |   2 +
 2 files changed, 102 insertions(+), 21 deletions(-)

diff --git a/emulator/bthost.c b/emulator/bthost.c
index 66e337f34..ec7a7eb0d 100644
--- a/emulator/bthost.c
+++ b/emulator/bthost.c
@@ -203,6 +203,9 @@ struct bthost {
 	uint8_t bdaddr[6];
 	uint8_t features[8];
 	bthost_send_func send_handler;
+	uint16_t acl_len;
+	uint16_t l2_len;
+	void *acl_data;
 	void *send_data;
 	struct cmd_queue cmd_q;
 	uint8_t ncmd;
@@ -491,6 +494,7 @@ void bthost_destroy(struct bthost *bthost)
 
 	queue_destroy(bthost->le_ext_adv, le_ext_adv_free);
 
+	free(bthost->acl_data);
 	free(bthost);
 }
 
@@ -2426,24 +2430,15 @@ static void process_rfcomm(struct bthost *bthost, struct btconn *conn,
 	}
 }
 
-static void process_acl(struct bthost *bthost, const void *data, uint16_t len)
+static void process_l2cap(struct bthost *bthost, uint16_t handle,
+					const void *data, uint16_t len)
 {
-	const struct bt_hci_acl_hdr *acl_hdr = data;
-	const struct bt_l2cap_hdr *l2_hdr = data + sizeof(*acl_hdr);
-	uint16_t handle, cid, acl_len, l2_len;
+	const struct bt_l2cap_hdr *l2_hdr = data;
 	struct cid_hook *hook;
 	struct btconn *conn;
 	struct l2conn *l2conn;
-	const void *l2_data;
-
-	if (len < sizeof(*acl_hdr) + sizeof(*l2_hdr))
-		return;
-
-	acl_len = le16_to_cpu(acl_hdr->dlen);
-	if (len != sizeof(*acl_hdr) + acl_len)
-		return;
+	uint16_t cid, l2_len;
 
-	handle = acl_handle(acl_hdr->handle);
 	conn = bthost_find_conn(bthost, handle);
 	if (!conn) {
 		bthost_debug(bthost, "ACL data for unknown handle 0x%04x",
@@ -2452,36 +2447,40 @@ static void process_acl(struct bthost *bthost, const void *data, uint16_t len)
 	}
 
 	l2_len = le16_to_cpu(l2_hdr->len);
-	if (len - sizeof(*acl_hdr) != sizeof(*l2_hdr) + l2_len)
+	if (len != sizeof(*l2_hdr) + l2_len) {
+		bthost_debug(bthost, "L2CAP invalid length: %u != %zu",
+					len, sizeof(*l2_hdr) + l2_len);
 		return;
+	}
 
-	l2_data = data + sizeof(*acl_hdr) + sizeof(*l2_hdr);
+	bthost_debug(bthost, "L2CAP data: %u bytes", l2_len);
 
 	cid = le16_to_cpu(l2_hdr->cid);
 
 	hook = find_cid_hook(conn, cid);
 	if (hook) {
-		hook->func(l2_data, l2_len, hook->user_data);
+		hook->func(l2_hdr->data, l2_len, hook->user_data);
 		return;
 	}
 
 	switch (cid) {
 	case 0x0001:
-		l2cap_sig(bthost, conn, l2_data, l2_len);
+		l2cap_sig(bthost, conn, l2_hdr->data, l2_len);
 		break;
 	case 0x0005:
-		l2cap_le_sig(bthost, conn, l2_data, l2_len);
+		l2cap_le_sig(bthost, conn, l2_hdr->data, l2_len);
 		break;
 	case 0x0006:
-		smp_data(conn->smp_data, l2_data, l2_len);
+		smp_data(conn->smp_data, l2_hdr->data, l2_len);
 		break;
 	case 0x0007:
-		smp_bredr_data(conn->smp_data, l2_data, l2_len);
+		smp_bredr_data(conn->smp_data, l2_hdr->data, l2_len);
 		break;
 	default:
 		l2conn = btconn_find_l2cap_conn_by_scid(conn, cid);
 		if (l2conn && l2conn->psm == 0x0003)
-			process_rfcomm(bthost, conn, l2conn, l2_data, l2_len);
+			process_rfcomm(bthost, conn, l2conn, l2_hdr->data,
+								l2_len);
 		else
 			bthost_debug(bthost,
 					"Packet for unknown CID 0x%04x (%u)",
@@ -2490,6 +2489,86 @@ static void process_acl(struct bthost *bthost, const void *data, uint16_t len)
 	}
 }
 
+static void append_acl_data(struct bthost *bthost, uint16_t handle,
+				uint8_t flags, const void *data, uint16_t len)
+{
+	if (!bthost->acl_data) {
+		bthost_debug(bthost, "Unexpected ACL frame: handle 0x%4.4x "
+				"flags 0x%2.2x", handle, flags);
+		return;
+	}
+
+	if (bthost->acl_len + len > bthost->l2_len) {
+		bthost_debug(bthost, "Unexpected ACL frame: handle 0x%4.4x "
+				"flags 0x%2.2x", handle, flags);
+		return;
+	}
+
+	memcpy(bthost->acl_data + bthost->acl_len, data, len);
+	bthost->acl_len += len;
+
+	bthost_debug(bthost, "ACL data: %u/%u bytes", bthost->acl_len,
+							bthost->l2_len);
+
+	if (bthost->acl_len < bthost->l2_len)
+		return;
+
+	process_l2cap(bthost, handle, bthost->acl_data, bthost->acl_len);
+
+	free(bthost->acl_data);
+	bthost->acl_data = NULL;
+	bthost->acl_len = 0;
+	bthost->l2_len = 0;
+}
+
+static void process_acl(struct bthost *bthost, const void *data, uint16_t len)
+{
+	const struct bt_hci_acl_hdr *acl_hdr = data;
+	const struct bt_l2cap_hdr *l2_hdr = (void *) acl_hdr->data;
+	uint16_t handle, acl_len, l2_len;
+	uint8_t flags;
+
+	acl_len = le16_to_cpu(acl_hdr->dlen);
+	if (len != sizeof(*acl_hdr) + acl_len)
+		return;
+
+	handle = acl_handle(acl_hdr->handle);
+	flags = acl_flags(acl_hdr->handle);
+
+	switch (flags) {
+	case 0x00:	/* start of a non-automatically-flushable PDU */
+	case 0x02:	/* start of an automatically-flushable PDU */
+		if (bthost->acl_data) {
+			bthost_debug(bthost, "Unexpected ACL start frame");
+			free(bthost->acl_data);
+			bthost->acl_data = NULL;
+			bthost->acl_len = 0;
+		}
+
+		l2_len = le16_to_cpu(l2_hdr->len) + sizeof(*l2_hdr);
+
+		bthost_debug(bthost, "acl_len %u l2_len %u", acl_len, l2_len);
+
+		if (acl_len == l2_len) {
+			process_l2cap(bthost, handle, acl_hdr->data, acl_len);
+			break;
+		}
+
+		bthost->acl_data = malloc(l2_len);
+		bthost->acl_len = 0;
+		bthost->l2_len = l2_len;
+		/* fall through */
+	case 0x01:	/* continuing fragment */
+		append_acl_data(bthost, handle, flags, acl_hdr->data, acl_len);
+		break;
+	case 0x03:	/* complete automatically-flushable PDU */
+		process_l2cap(bthost, handle, acl_hdr->data, acl_len);
+		break;
+	default:
+		bthost_debug(bthost, "Invalid ACL frame flags 0x%2.2x", flags);
+	}
+}
+
 void bthost_receive_h4(struct bthost *bthost, const void *data, uint16_t len)
 {
 	uint8_t pkt_type;
diff --git a/monitor/bt.h b/monitor/bt.h
index dc4e11c3c..66ed3ef5a 100644
--- a/monitor/bt.h
+++ b/monitor/bt.h
@@ -488,6 +488,7 @@ struct bt_hci_cmd_hdr {
 struct bt_hci_acl_hdr {
 	uint16_t handle;
 	uint16_t dlen;
+	uint8_t  data[];
 } __attribute__ ((packed));
 
 struct bt_hci_sco_hdr {
@@ -3663,6 +3664,7 @@ struct bt_hci_evt_le_req_peer_sca_complete {
 struct bt_l2cap_hdr {
 	uint16_t len;
 	uint16_t cid;
+	uint8_t  data[];
 } __attribute__ ((packed));
 
 struct bt_l2cap_hdr_sig {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-02-10 22:31 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-09 23:55 [PATCH BlueZ 1/4] bthost: Fix not handling ACL fragmentation Luiz Augusto von Dentz
2022-02-09 23:55 ` [PATCH BlueZ 2/4] bthost: Fix length calculation for RFCOMM header Luiz Augusto von Dentz
2022-02-09 23:55 ` [PATCH BlueZ 3/4] bthost: Increase number of credits Luiz Augusto von Dentz
2022-02-09 23:55 ` [PATCH BlueZ 4/4] rfcomm-tester: Add test to write big chunks of data Luiz Augusto von Dentz
2022-02-10  5:34 ` [BlueZ,1/4] bthost: Fix not handling ACL fragmentation bluez.test.bot
2022-02-10 22:31   ` Luiz Augusto von Dentz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.