Proper git format-patch -- Luiz Augusto von Dentz Engenheiro de Computação
From b21dba4db747fc1a2329edbd216567de20936fc6 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz <luiz.dentz@xxxxxxxxxxxxx> Date: Mon, 22 Jun 2009 20:06:04 -0300 Subject: [PATCH] bluetooth: Fix rejected connection to not disconnect ACL. When using DEFER_SETUP on a RFCOMM socket a SABM frame triggers authorization which when rejected send a DM as response. This is fine accourding 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@xxxxxxxxxxxxx> --- include/net/bluetooth/rfcomm.h | 13 ++++++----- net/bluetooth/rfcomm/core.c | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 8007261..72b92c5 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -152,12 +152,13 @@ struct rfcomm_msc { /* ---- Core structures, flags etc ---- */ struct rfcomm_session { - struct list_head list; - struct socket *sock; - unsigned long state; - unsigned long flags; - atomic_t refcnt; - int initiator; + struct list_head list; + struct socket *sock; + struct timer_list timer; + unsigned long state; + unsigned long flags; + atomic_t refcnt; + int initiator; /* Default DLC parameters */ int cfc; diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 374536e..7fa2949 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_DISC_TIMEOUT); + rfcomm_session_put(s); } @@ -454,6 +485,7 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) rfcomm_schedule(RFCOMM_SCHED_AUTH); break; } + /* Fall through */ default: @@ -567,6 +599,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; @@ -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); } @@ -1774,6 +1809,7 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) rfcomm_send_dm(s, d->dlci); else d->state = BT_CLOSED; + __rfcomm_dlc_close(d, ECONNREFUSED); continue; } @@ -1879,6 +1915,11 @@ static inline void rfcomm_process_sessions(void) struct rfcomm_session *s; s = list_entry(p, struct rfcomm_session, list); + if (test_bit(RFCOMM_TIMED_OUT, &s->flags)) { + rfcomm_session_put(s); + continue; + } + if (s->state == BT_LISTEN) { rfcomm_accept_connection(s); continue; -- 1.6.3.1