public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
From: "Martin Röhricht" <ml@felicis.org>
To: bluez-devel@lists.sourceforge.net
Subject: [Bluez-devel] L2CAP Flow Control
Date: Thu, 09 Feb 2006 11:59:56 +0100	[thread overview]
Message-ID: <1139482797.2349.32.camel@localhost.localdomain> (raw)

[-- Attachment #1: Type: text/plain, Size: 2818 bytes --]

Hello together,

I'm currently trying to implement Flow Control on the L2CAP layer. I
already made some good progress but before things become too complicated
all of a sudden, I send my first lines of code to the list to give
everyone the opportuntiy to get an understanding of what I am doing.
So this »patch« doesn't bring Flow Control into play, yet. But it
shouldn't break connections either.

I am only working on l2cap.h and l2cap.c
I added some basic constants to the header file as well as two macros
»SET_BIT« and »HAS_BIT« which I need later to determine whether one of
the four possible options in a request has already been seen in this
request (imagine a request consisting of MTU + QoS + MTU). Further more
I added all the different parameter fields for our channel and socket
(struct l2cap_pinfo): o stands for outgoing, i for incoming and conf
reflects the parameters sent in a configuration request. Later on we
will have to deal with these conf parameters, compare them to reasonable
values and so on.
In l2cap.c I removed the function »l2cap_get_conf_opt« because I think
we would run into trouble on Big Endian machines with this code. The
problem is that the basic MTU option consists of only one 16 bit wide
parameter so we can use __le16_to_cpu() for that, but in case of QoS or
my Flow Control, we have a bunch of unfortunately unaligned data fields.
Therefore I access those data fields directly (e.g. cap->conf_mode =
*(__u8*)(ptr + 2);). 
The result flag corresponds to the result values of a configuration
request. We need more than just one, because to be standard compliant we
need to let the other side know, which options are unknown (in such a
case) or which parameters are unacceptable of which particular option.
So we have one basic result code for our responds (result[0]) -- this is
like the max function of {success; Failure/unacceptable parameters;
Failure/rejected; Failure/unknown options}. But we need to keep track of
our failures for a good responds.
The while loop extracts all options of a single request. We have some
basic error check mechanisms to avoid buffer overflow attacks by
corrupted requests.
Inside each option parser we use the HAS_BIT macro to make sure we will
only consider the first of possible multiple options of similar type
within one request. The hint is not processed yet.

This is only basic stuff to detect the different options and parse them
correctly. Now it becomes more complicated as we have to keep track of
all possible failures, parameters, result codes etc. I would write a
function l2cap_check_conf_opt(struct sock *sk, int option) to check the
individual parameters of a particular option. It gets harder to build
the configuration response individually.

That's it for now.

Martin

[-- Attachment #2: patch-2.6.14-mr --]
[-- Type: text/plain, Size: 6355 bytes --]

--- linux-2.6.14-mh2/include/net/bluetooth/l2cap.h	2005-12-23 21:14:59.000000000 +0100
+++ linux-2.6.14-mr/include/net/bluetooth/l2cap.h	2006-02-09 10:54:57.000000000 +0100
@@ -28,6 +28,8 @@
 /* L2CAP defaults */
 #define L2CAP_DEFAULT_MTU	672
 #define L2CAP_DEFAULT_FLUSH_TO	0xFFFF
+#define L2CAP_MIN_TXW		1
+#define L2CAP_MAX_TXW		32
 
 #define L2CAP_CONN_TIMEOUT	(HZ * 40)
 
@@ -47,6 +49,10 @@
 	__u8  mode;
 };
 
+#define L2CAP_MODE_BASIC	0x00
+#define L2CAP_MDOE_RET		0x01
+#define L2CAP_MODE_FLOW		0x02
+
 #define L2CAP_CONNINFO	0x02
 struct l2cap_conninfo {
 	__u16 hci_handle;
@@ -129,8 +135,10 @@
 	__u8       data[0];
 } __attribute__ ((packed));
 
-#define L2CAP_CONF_SUCCESS	0x00
-#define L2CAP_CONF_UNACCEPT	0x01
+#define L2CAP_CONF_SUCCESS	0x0000
+#define L2CAP_CONF_UNACCEPT	0x0001
+#define L2CAP_CONF_REJ		0x0002
+#define L2CAP_CONF_UNKNOWN	0x0003
 
 struct l2cap_conf_opt {
 	__u8       type;
@@ -144,7 +152,8 @@
 #define L2CAP_CONF_QOS		0x03
 #define L2CAP_CONF_RFC		0x04
 
-#define L2CAP_CONF_MAX_SIZE	22
+#define SET_BIT(x, v) ((x) |= 1 << (v))
+#define HAS_BIT(x, v) (((x) & (1 << (v))) != 0)
 
 struct l2cap_disconn_req {
 	__le16     dcid;
@@ -220,6 +229,31 @@
 	__u8		conf_retry;
 	__u16		conf_mtu;
 
+	/* Configuration Request RFC Options */
+	__u8		conf_mode;
+	__u8		conf_txw;
+	__u8		conf_maxt;
+	__u16		conf_ret_to;
+	__u16		conf_mon_to;
+	__u16		conf_mps;
+	
+	/* incoming RFC Options */
+	__u8		imode;
+	__u8		itxw;
+	__u8		imaxt;
+	__u16		iret_to;
+	__u16		imon_to;
+	__u16		imps;
+
+	/* outgoing RFC Options */
+	__u8		omode;
+	__u8		otxw;
+	__u8		omaxt;
+	__u16		oret_to;
+	__u16		omon_to;
+	__u16		omps;
+
+	
 	__u8		ident;
 
 	struct l2cap_conn	*conn;
--- linux-2.6.14-mh2/net/bluetooth/l2cap.c	2006-01-20 12:05:17.000000000 +0100
+++ linux-2.6.14-mr/net/bluetooth/l2cap.c	2006-02-09 11:26:17.000000000 +0100
@@ -850,7 +850,7 @@
 		opts.imtu     = l2cap_pi(sk)->imtu;
 		opts.omtu     = l2cap_pi(sk)->omtu;
 		opts.flush_to = l2cap_pi(sk)->flush_to;
-		opts.mode     = 0x00;
+		opts.mode     = L2CAP_MODE_BASIC;
 
 		len = min_t(unsigned int, len, sizeof(opts));
 		if (copy_to_user(optval, (char *) &opts, len))
@@ -1236,74 +1236,105 @@
 	return NULL;
 }
 
-static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val)
-{
-	struct l2cap_conf_opt *opt = *ptr;
-	int len;
-
-	len = L2CAP_CONF_OPT_SIZE + opt->len;
-	*ptr += len;
-
-	*type = opt->type;
-	*olen = opt->len;
-
-	switch (opt->len) {
-	case 1:
-		*val = *((__u8 *) opt->val);
-		break;
-
-	case 2:
-		*val = __le16_to_cpu(*((__le16 *) opt->val));
-		break;
-
-	case 4:
-		*val = __le32_to_cpu(*((__le32 *) opt->val));
-		break;
-
-	default:
-		*val = (unsigned long) opt->val;
-		break;
-	}
-
-	BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val);
-	return len;
-}
 
 static inline void l2cap_parse_conf_req(struct sock *sk, void *data, u16 len)
 {
-	int type, hint, olen; 
-	unsigned long val;
-	void *ptr = data;
-
+	struct l2cap_pinfo *cap = l2cap_pi(sk);
+	int type, hint; 
+	u8 *ptr = data;
+	u8 options = 0; /* bit flag that indicates which options are set */
+	u8 result[5]; /* result codes for all 4 possible options plus result[0] */
+	unsigned int i;
+	
 	BT_DBG("sk %p len %d", sk, len);
 
+	for (i = 0; i < len; ++i)
+		printk("%02X ", ptr[i]);
+	printk("\n");
+
+	result[0] = L2CAP_CONF_SUCCESS;
+	
 	while (len >= L2CAP_CONF_OPT_SIZE) {
-		len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val);
 		u8 req_len = *(u8*)(ptr + 1);
 
+		/* Bail out if req length > packet length */
+		if (len < req_len) return;
+
+		len -= req_len + 2;
+		type = *(u8*)(ptr + 0);
+
 		hint  = type & 0x80;
 		type &= 0x7f;
 
 		BT_DBG("Type: %d len: %d", type, req_len);
+		
 		switch (type) {
 		case L2CAP_CONF_MTU:
-			l2cap_pi(sk)->conf_mtu = val;
+			if (HAS_BIT(options, L2CAP_CONF_MTU)) break;
+			if (req_len != 2) {
+				result[0] = L2CAP_CONF_REJ;
+				break;
+			}
+			cap->conf_mtu = __le16_to_cpup((__le16*)(ptr + 2));
+			SET_BIT(options, L2CAP_CONF_MTU);
 			break;
 
+		/* Flush TO not supported yet */
 		case L2CAP_CONF_FLUSH_TO:
-			l2cap_pi(sk)->flush_to = val;
+			if (HAS_BIT(options, L2CAP_CONF_FLUSH_TO)) break;
+#if 0
+			if (req_len != 2) {
+				result[0] = L2CAP_CONF_REJ;
+				break;
+			}
+			cap->flush_to = __le16_to_cpup((__le16*)(ptr + 2));
+#endif
+			result[0] = L2CAP_CONF_UNKNOWN;
+			result[L2CAP_CONF_FLUSH_TO] = L2CAP_CONF_UNKNOWN;
+			SET_BIT(options, L2CAP_CONF_FLUSH_TO);
 			break;
 
+		/* QoS not supported yet */
 		case L2CAP_CONF_QOS:
+			if (HAS_BIT(options, L2CAP_CONF_QOS)) break;
+#if 0
+			if (req_len != 22) {
+				result[0] = L2CAP_CONF_REJ;
+				break;
+			}
+#endif
+			result[0] = L2CAP_CONF_UNKNOWN;
+			result[L2CAP_CONF_QOS] = L2CAP_CONF_UNKNOWN;
+			SET_BIT(options, L2CAP_CONF_QOS);
 			break;
 
+		case L2CAP_CONF_RFC: {
+			if (HAS_BIT(options, L2CAP_CONF_RFC)) break;
+			if (req_len != 9) {
+				result[0] = L2CAP_CONF_REJ;
+				break;
+			}
+			cap->conf_mode = *(__u8*)(ptr + 2);
+			cap->conf_txw  = *(__u8*)(ptr + 3);
+			cap->conf_maxt = *(__u8*)(ptr + 4);
+			cap->conf_ret_to = __le16_to_cpup((__le16*)(ptr + 5));
+			cap->conf_mon_to = __le16_to_cpup((__le16*)(ptr + 7));
+			cap->conf_mps    = __le16_to_cpup((__le16*)(ptr + 9));
+			SET_BIT(options, L2CAP_CONF_RFC);
+			break;
+		}
+	
 		default:
+			result[0] = L2CAP_CONF_REJ;
 			if (hint)
 				break;
 
 			/* FIXME: Reject unknown option */
 			break;
 		}
+
+		/* Go to start of (possible) next request */
+		ptr += req_len;
 	}
 }
 
@@ -1361,16 +1392,15 @@
 static inline int l2cap_conf_output(struct sock *sk, void **ptr)
 {
 	struct l2cap_pinfo *pi = l2cap_pi(sk);
-	int result = 0;
+	int result = L2CAP_CONF_SUCCESS;
 
 	/* Configure output options and let the other side know
 	 * which ones we don't like. */
 	if (pi->conf_mtu < pi->omtu) {
 		l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, pi->omtu);
 		result = L2CAP_CONF_UNACCEPT;
-	} else {
+	} else
 		pi->omtu = pi->conf_mtu;
-	}
 
 	BT_DBG("sk %p result %d", sk, result);
 	return result;
@@ -1548,7 +1578,7 @@
 	l2cap_parse_conf_req(sk, req->data, cmd_len - sizeof(*req));
 
 	if (flags & 0x0001) {
-		/* Incomplete config. Send empty response. */
+		/* Incomplete config (not supported). Send empty response. */
 		l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
 				l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
 		goto unlock;

             reply	other threads:[~2006-02-09 10:59 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-02-09 10:59 Martin Röhricht [this message]
2006-02-13 18:10 ` [Bluez-devel] L2CAP Flow Control Martin Röhricht
  -- strict thread matches above, loose matches on Subject: below --
2006-09-15 20:30 Martin Röhricht
2006-09-15 21:12 ` Marcel Holtmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1139482797.2349.32.camel@localhost.localdomain \
    --to=ml@felicis.org \
    --cc=bluez-devel@lists.sourceforge.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox