[PATCH BlueZ 1/4] bthost: Fix not handling ACL fragmentation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

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




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux