From 9e726b17422bade75fba94e625cd35fd1353e682 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 15 Jul 2009 13:50:58 -0300 Subject: 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 Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 2 ++ net/bluetooth/rfcomm/core.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index c274993234e..921d7b3c7f8 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -29,6 +29,7 @@ #define RFCOMM_CONN_TIMEOUT (HZ * 30) #define RFCOMM_DISC_TIMEOUT (HZ * 20) #define RFCOMM_AUTH_TIMEOUT (HZ * 25) +#define RFCOMM_IDLE_TIMEOUT (HZ * 2) #define RFCOMM_DEFAULT_MTU 127 #define RFCOMM_DEFAULT_CREDITS 7 @@ -154,6 +155,7 @@ struct rfcomm_msc { struct rfcomm_session { struct list_head list; struct socket *sock; + struct timer_list timer; unsigned long state; unsigned long flags; atomic_t refcnt; 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) auth_type); } +static void rfcomm_session_timeout(unsigned long arg) +{ + struct rfcomm_session *s = (void *) arg; + + BT_DBG("session %p state %ld", s, s->state); + + set_bit(RFCOMM_TIMED_OUT, &s->flags); + rfcomm_session_put(s); + rfcomm_schedule(RFCOMM_SCHED_TIMEO); +} + +static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout) +{ + BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout); + + if (!mod_timer(&s->timer, jiffies + timeout)) + rfcomm_session_hold(s); +} + +static void rfcomm_session_clear_timer(struct rfcomm_session *s) +{ + BT_DBG("session %p state %ld", s, s->state); + + if (timer_pending(&s->timer) && del_timer(&s->timer)) + rfcomm_session_put(s); +} + /* ---- RFCOMM DLCs ---- */ static void rfcomm_dlc_timeout(unsigned long arg) { @@ -320,6 +347,7 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) rfcomm_session_hold(s); + rfcomm_session_clear_timer(s); rfcomm_dlc_hold(d); list_add(&d->list, &s->dlcs); d->session = s; @@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) d->session = NULL; rfcomm_dlc_put(d); + if (list_empty(&s->dlcs)) + rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT); + rfcomm_session_put(s); } @@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) BT_DBG("session %p sock %p", s, sock); + setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s); + INIT_LIST_HEAD(&s->dlcs); s->state = state; s->sock = sock; @@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s) if (state == BT_CONNECTED) rfcomm_send_disc(s, 0); + rfcomm_session_clear_timer(s); sock_release(s->sock); kfree(s); @@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err) __rfcomm_dlc_close(d, err); } + rfcomm_session_clear_timer(s); rfcomm_session_put(s); } @@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void) struct rfcomm_session *s; s = list_entry(p, struct rfcomm_session, list); + if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) { + s->state = BT_DISCONN; + rfcomm_send_disc(s, 0); + continue; + } + if (s->state == BT_LISTEN) { rfcomm_accept_connection(s); continue; -- cgit v1.2.3-18-g5258