Commit 9e726b17 authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz Committed by Marcel Holtmann

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: default avatarLuiz Augusto von Dentz <luiz.dentz@openbossa.org>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent ef54fd93
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#define RFCOMM_CONN_TIMEOUT (HZ * 30) #define RFCOMM_CONN_TIMEOUT (HZ * 30)
#define RFCOMM_DISC_TIMEOUT (HZ * 20) #define RFCOMM_DISC_TIMEOUT (HZ * 20)
#define RFCOMM_AUTH_TIMEOUT (HZ * 25) #define RFCOMM_AUTH_TIMEOUT (HZ * 25)
#define RFCOMM_IDLE_TIMEOUT (HZ * 2)
#define RFCOMM_DEFAULT_MTU 127 #define RFCOMM_DEFAULT_MTU 127
#define RFCOMM_DEFAULT_CREDITS 7 #define RFCOMM_DEFAULT_CREDITS 7
...@@ -154,6 +155,7 @@ struct rfcomm_msc { ...@@ -154,6 +155,7 @@ struct rfcomm_msc {
struct rfcomm_session { struct rfcomm_session {
struct list_head list; struct list_head list;
struct socket *sock; struct socket *sock;
struct timer_list timer;
unsigned long state; unsigned long state;
unsigned long flags; unsigned long flags;
atomic_t refcnt; atomic_t refcnt;
......
...@@ -244,6 +244,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d) ...@@ -244,6 +244,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d)
auth_type); 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 ---- */ /* ---- RFCOMM DLCs ---- */
static void rfcomm_dlc_timeout(unsigned long arg) 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) ...@@ -320,6 +347,7 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
rfcomm_session_hold(s); rfcomm_session_hold(s);
rfcomm_session_clear_timer(s);
rfcomm_dlc_hold(d); rfcomm_dlc_hold(d);
list_add(&d->list, &s->dlcs); list_add(&d->list, &s->dlcs);
d->session = s; d->session = s;
...@@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) ...@@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
d->session = NULL; d->session = NULL;
rfcomm_dlc_put(d); rfcomm_dlc_put(d);
if (list_empty(&s->dlcs))
rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT);
rfcomm_session_put(s); rfcomm_session_put(s);
} }
...@@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) ...@@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
BT_DBG("session %p sock %p", s, sock); BT_DBG("session %p sock %p", s, sock);
setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s);
INIT_LIST_HEAD(&s->dlcs); INIT_LIST_HEAD(&s->dlcs);
s->state = state; s->state = state;
s->sock = sock; s->sock = sock;
...@@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s) ...@@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s)
if (state == BT_CONNECTED) if (state == BT_CONNECTED)
rfcomm_send_disc(s, 0); rfcomm_send_disc(s, 0);
rfcomm_session_clear_timer(s);
sock_release(s->sock); sock_release(s->sock);
kfree(s); kfree(s);
...@@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err) ...@@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err)
__rfcomm_dlc_close(d, err); __rfcomm_dlc_close(d, err);
} }
rfcomm_session_clear_timer(s);
rfcomm_session_put(s); rfcomm_session_put(s);
} }
...@@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void) ...@@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void)
struct rfcomm_session *s; struct rfcomm_session *s;
s = list_entry(p, struct rfcomm_session, list); 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) { if (s->state == BT_LISTEN) {
rfcomm_accept_connection(s); rfcomm_accept_connection(s);
continue; continue;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment