diff options
author | Luiz Augusto von Dentz <luiz.dentz@openbossa.org> | 2009-07-15 12:50:58 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2009-08-22 18:05:58 -0400 |
commit | 9e726b17422bade75fba94e625cd35fd1353e682 (patch) | |
tree | af8cf616d9cbda12f51682dbb96e696f135be436 /net/bluetooth | |
parent | ef54fd937fbd5ebaeb023818524565bd526a5f36 (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')
-rw-r--r-- | net/bluetooth/rfcomm/core.c | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 26af4854af64..25692bc0a342 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 | ||
247 | static 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 | |||
258 | static 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 | |||
266 | static 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 ---- */ |
248 | static void rfcomm_dlc_timeout(unsigned long arg) | 275 | static 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; |