aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/rfcomm/core.c
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.dentz@openbossa.org>2009-07-15 12:50:58 -0400
committerMarcel Holtmann <marcel@holtmann.org>2009-08-22 18:05:58 -0400
commit9e726b17422bade75fba94e625cd35fd1353e682 (patch)
treeaf8cf616d9cbda12f51682dbb96e696f135be436 /net/bluetooth/rfcomm/core.c
parentef54fd937fbd5ebaeb023818524565bd526a5f36 (diff)
Bluetooth: Fix rejected connection not disconnecting ACL link
When using DEFER_SETUP on a RFCOMM socket, a SABM frame triggers authorization which when rejected send a DM response. This is fine according to the RFCOMM spec: the responding implementation may replace the "proper" response on the Multiplexer Control channel with a DM frame, sent on the referenced DLCI to indicate that the DLCI is not open, and that the responder would not grant a request to open it later either. But some stacks doesn't seems to cope with this leaving DLCI 0 open after receiving DM frame. To fix it properly a timer was introduced to rfcomm_session which is used to set a timeout when the last active DLC of a session is unlinked, this will give the remote stack some time to reply with a proper DISC frame on DLCI 0 avoiding both sides sending DISC to each other on stacks that follow the specification and taking care of those who don't by taking down DLCI 0. Signed-off-by: Luiz Augusto von Dentz <luiz.dentz@openbossa.org> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/rfcomm/core.c')
-rw-r--r--net/bluetooth/rfcomm/core.c41
1 files changed, 41 insertions, 0 deletions
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 26af4854af6..25692bc0a34 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -244,6 +244,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d)
244 auth_type); 244 auth_type);
245} 245}
246 246
247static void rfcomm_session_timeout(unsigned long arg)
248{
249 struct rfcomm_session *s = (void *) arg;
250
251 BT_DBG("session %p state %ld", s, s->state);
252
253 set_bit(RFCOMM_TIMED_OUT, &s->flags);
254 rfcomm_session_put(s);
255 rfcomm_schedule(RFCOMM_SCHED_TIMEO);
256}
257
258static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout)
259{
260 BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout);
261
262 if (!mod_timer(&s->timer, jiffies + timeout))
263 rfcomm_session_hold(s);
264}
265
266static void rfcomm_session_clear_timer(struct rfcomm_session *s)
267{
268 BT_DBG("session %p state %ld", s, s->state);
269
270 if (timer_pending(&s->timer) && del_timer(&s->timer))
271 rfcomm_session_put(s);
272}
273
247/* ---- RFCOMM DLCs ---- */ 274/* ---- RFCOMM DLCs ---- */
248static void rfcomm_dlc_timeout(unsigned long arg) 275static void rfcomm_dlc_timeout(unsigned long arg)
249{ 276{
@@ -320,6 +347,7 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
320 347
321 rfcomm_session_hold(s); 348 rfcomm_session_hold(s);
322 349
350 rfcomm_session_clear_timer(s);
323 rfcomm_dlc_hold(d); 351 rfcomm_dlc_hold(d);
324 list_add(&d->list, &s->dlcs); 352 list_add(&d->list, &s->dlcs);
325 d->session = s; 353 d->session = s;
@@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
335 d->session = NULL; 363 d->session = NULL;
336 rfcomm_dlc_put(d); 364 rfcomm_dlc_put(d);
337 365
366 if (list_empty(&s->dlcs))
367 rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT);
368
338 rfcomm_session_put(s); 369 rfcomm_session_put(s);
339} 370}
340 371
@@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
567 598
568 BT_DBG("session %p sock %p", s, sock); 599 BT_DBG("session %p sock %p", s, sock);
569 600
601 setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s);
602
570 INIT_LIST_HEAD(&s->dlcs); 603 INIT_LIST_HEAD(&s->dlcs);
571 s->state = state; 604 s->state = state;
572 s->sock = sock; 605 s->sock = sock;
@@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s)
598 if (state == BT_CONNECTED) 631 if (state == BT_CONNECTED)
599 rfcomm_send_disc(s, 0); 632 rfcomm_send_disc(s, 0);
600 633
634 rfcomm_session_clear_timer(s);
601 sock_release(s->sock); 635 sock_release(s->sock);
602 kfree(s); 636 kfree(s);
603 637
@@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err)
639 __rfcomm_dlc_close(d, err); 673 __rfcomm_dlc_close(d, err);
640 } 674 }
641 675
676 rfcomm_session_clear_timer(s);
642 rfcomm_session_put(s); 677 rfcomm_session_put(s);
643} 678}
644 679
@@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void)
1879 struct rfcomm_session *s; 1914 struct rfcomm_session *s;
1880 s = list_entry(p, struct rfcomm_session, list); 1915 s = list_entry(p, struct rfcomm_session, list);
1881 1916
1917 if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) {
1918 s->state = BT_DISCONN;
1919 rfcomm_send_disc(s, 0);
1920 continue;
1921 }
1922
1882 if (s->state == BT_LISTEN) { 1923 if (s->state == BT_LISTEN) {
1883 rfcomm_accept_connection(s); 1924 rfcomm_accept_connection(s);
1884 continue; 1925 continue;