forked from mirrors/linux
		
	Bluetooth: Add support for deferring RFCOMM connection setup
In order to decide if listening RFCOMM sockets should be accept()ed the BD_ADDR of the remote device needs to be known. This patch adds a socket option which defines a timeout for deferring the actual connection setup. The connection setup is done after reading from the socket for the first time. Until then writing to the socket returns ENOTCONN. Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
		
							parent
							
								
									c4f912e155
								
							
						
					
					
						commit
						bb23c0ab82
					
				
					 3 changed files with 88 additions and 18 deletions
				
			
		| 
						 | 
					@ -185,6 +185,7 @@ struct rfcomm_dlc {
 | 
				
			||||||
	u8            out;
 | 
						u8            out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u32           link_mode;
 | 
						u32           link_mode;
 | 
				
			||||||
 | 
						u32           defer_setup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uint          mtu;
 | 
						uint          mtu;
 | 
				
			||||||
	uint          cfc;
 | 
						uint          cfc;
 | 
				
			||||||
| 
						 | 
					@ -206,6 +207,7 @@ struct rfcomm_dlc {
 | 
				
			||||||
#define RFCOMM_AUTH_PENDING 4
 | 
					#define RFCOMM_AUTH_PENDING 4
 | 
				
			||||||
#define RFCOMM_AUTH_ACCEPT  5
 | 
					#define RFCOMM_AUTH_ACCEPT  5
 | 
				
			||||||
#define RFCOMM_AUTH_REJECT  6
 | 
					#define RFCOMM_AUTH_REJECT  6
 | 
				
			||||||
 | 
					#define RFCOMM_DEFER_SETUP  7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Scheduling flags and events */
 | 
					/* Scheduling flags and events */
 | 
				
			||||||
#define RFCOMM_SCHED_STATE  0
 | 
					#define RFCOMM_SCHED_STATE  0
 | 
				
			||||||
| 
						 | 
					@ -239,6 +241,7 @@ int  rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
 | 
				
			||||||
int  rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
 | 
					int  rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
 | 
				
			||||||
int  rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig);
 | 
					int  rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig);
 | 
				
			||||||
int  rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
 | 
					int  rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
 | 
				
			||||||
 | 
					void rfcomm_dlc_accept(struct rfcomm_dlc *d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define rfcomm_dlc_lock(d)     spin_lock(&d->lock)
 | 
					#define rfcomm_dlc_lock(d)     spin_lock(&d->lock)
 | 
				
			||||||
#define rfcomm_dlc_unlock(d)   spin_unlock(&d->lock)
 | 
					#define rfcomm_dlc_unlock(d)   spin_unlock(&d->lock)
 | 
				
			||||||
| 
						 | 
					@ -333,7 +336,6 @@ struct rfcomm_dev_req {
 | 
				
			||||||
	bdaddr_t src;
 | 
						bdaddr_t src;
 | 
				
			||||||
	bdaddr_t dst;
 | 
						bdaddr_t dst;
 | 
				
			||||||
	u8       channel;
 | 
						u8       channel;
 | 
				
			||||||
	
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct rfcomm_dev_info {
 | 
					struct rfcomm_dev_info {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -421,9 +421,16 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
 | 
				
			||||||
			d, d->state, d->dlci, err, s);
 | 
								d, d->state, d->dlci, err, s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (d->state) {
 | 
						switch (d->state) {
 | 
				
			||||||
	case BT_CONNECTED:
 | 
					 | 
				
			||||||
	case BT_CONFIG:
 | 
					 | 
				
			||||||
	case BT_CONNECT:
 | 
						case BT_CONNECT:
 | 
				
			||||||
 | 
						case BT_CONFIG:
 | 
				
			||||||
 | 
							if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
 | 
				
			||||||
 | 
								set_bit(RFCOMM_AUTH_REJECT, &d->flags);
 | 
				
			||||||
 | 
								rfcomm_schedule(RFCOMM_SCHED_AUTH);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							/* Fall through */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case BT_CONNECTED:
 | 
				
			||||||
		d->state = BT_DISCONN;
 | 
							d->state = BT_DISCONN;
 | 
				
			||||||
		if (skb_queue_empty(&d->tx_queue)) {
 | 
							if (skb_queue_empty(&d->tx_queue)) {
 | 
				
			||||||
			rfcomm_send_disc(s, d->dlci);
 | 
								rfcomm_send_disc(s, d->dlci);
 | 
				
			||||||
| 
						 | 
					@ -434,6 +441,14 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case BT_OPEN:
 | 
				
			||||||
 | 
							if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
 | 
				
			||||||
 | 
								set_bit(RFCOMM_AUTH_REJECT, &d->flags);
 | 
				
			||||||
 | 
								rfcomm_schedule(RFCOMM_SCHED_AUTH);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							/* Fall through */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		rfcomm_dlc_clear_timer(d);
 | 
							rfcomm_dlc_clear_timer(d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1162,7 +1177,7 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
 | 
					void rfcomm_dlc_accept(struct rfcomm_dlc *d)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sock *sk = d->session->sock->sk;
 | 
						struct sock *sk = d->session->sock->sk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1181,6 +1196,20 @@ static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
 | 
				
			||||||
	rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
 | 
						rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void rfcomm_check_accept(struct rfcomm_dlc *d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (rfcomm_check_link_mode(d)) {
 | 
				
			||||||
 | 
							set_bit(RFCOMM_AUTH_PENDING, &d->flags);
 | 
				
			||||||
 | 
							rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if (d->defer_setup) {
 | 
				
			||||||
 | 
								set_bit(RFCOMM_DEFER_SETUP, &d->flags);
 | 
				
			||||||
 | 
								rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
 | 
				
			||||||
 | 
							} else
 | 
				
			||||||
 | 
								rfcomm_dlc_accept(d);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
 | 
					static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rfcomm_dlc *d;
 | 
						struct rfcomm_dlc *d;
 | 
				
			||||||
| 
						 | 
					@ -1203,11 +1232,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
 | 
				
			||||||
	if (d) {
 | 
						if (d) {
 | 
				
			||||||
		if (d->state == BT_OPEN) {
 | 
							if (d->state == BT_OPEN) {
 | 
				
			||||||
			/* DLC was previously opened by PN request */
 | 
								/* DLC was previously opened by PN request */
 | 
				
			||||||
			if (rfcomm_check_link_mode(d)) {
 | 
								rfcomm_check_accept(d);
 | 
				
			||||||
				set_bit(RFCOMM_AUTH_PENDING, &d->flags);
 | 
					 | 
				
			||||||
				rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
 | 
					 | 
				
			||||||
			} else
 | 
					 | 
				
			||||||
				rfcomm_dlc_accept(d);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1219,11 +1244,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
 | 
				
			||||||
		d->addr = __addr(s->initiator, dlci);
 | 
							d->addr = __addr(s->initiator, dlci);
 | 
				
			||||||
		rfcomm_dlc_link(s, d);
 | 
							rfcomm_dlc_link(s, d);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (rfcomm_check_link_mode(d)) {
 | 
							rfcomm_check_accept(d);
 | 
				
			||||||
			set_bit(RFCOMM_AUTH_PENDING, &d->flags);
 | 
					 | 
				
			||||||
			rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
 | 
					 | 
				
			||||||
		} else
 | 
					 | 
				
			||||||
			rfcomm_dlc_accept(d);
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		rfcomm_send_dm(s, dlci);
 | 
							rfcomm_send_dm(s, dlci);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1717,8 +1738,13 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
 | 
				
			||||||
			if (d->out) {
 | 
								if (d->out) {
 | 
				
			||||||
				rfcomm_send_pn(s, 1, d);
 | 
									rfcomm_send_pn(s, 1, d);
 | 
				
			||||||
				rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
 | 
									rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
 | 
				
			||||||
			} else
 | 
								} else {
 | 
				
			||||||
				rfcomm_dlc_accept(d);
 | 
									if (d->defer_setup) {
 | 
				
			||||||
 | 
										set_bit(RFCOMM_DEFER_SETUP, &d->flags);
 | 
				
			||||||
 | 
										rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
 | 
				
			||||||
 | 
									} else
 | 
				
			||||||
 | 
										rfcomm_dlc_accept(d);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			if (d->link_mode & RFCOMM_LM_SECURE) {
 | 
								if (d->link_mode & RFCOMM_LM_SECURE) {
 | 
				
			||||||
				struct sock *sk = s->sock->sk;
 | 
									struct sock *sk = s->sock->sk;
 | 
				
			||||||
				hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon);
 | 
									hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -262,8 +262,10 @@ static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
 | 
				
			||||||
	if (parent) {
 | 
						if (parent) {
 | 
				
			||||||
		sk->sk_type = parent->sk_type;
 | 
							sk->sk_type = parent->sk_type;
 | 
				
			||||||
		pi->link_mode = rfcomm_pi(parent)->link_mode;
 | 
							pi->link_mode = rfcomm_pi(parent)->link_mode;
 | 
				
			||||||
 | 
							pi->dlc->defer_setup = bt_sk(parent)->defer_setup;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		pi->link_mode = 0;
 | 
							pi->link_mode = 0;
 | 
				
			||||||
 | 
							pi->dlc->defer_setup = 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pi->dlc->link_mode = pi->link_mode;
 | 
						pi->dlc->link_mode = pi->link_mode;
 | 
				
			||||||
| 
						 | 
					@ -554,6 +556,9 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 | 
				
			||||||
	struct sk_buff *skb;
 | 
						struct sk_buff *skb;
 | 
				
			||||||
	int sent = 0;
 | 
						int sent = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_bit(RFCOMM_DEFER_SETUP, &d->flags))
 | 
				
			||||||
 | 
							return -ENOTCONN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (msg->msg_flags & MSG_OOB)
 | 
						if (msg->msg_flags & MSG_OOB)
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -633,10 +638,16 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 | 
				
			||||||
			       struct msghdr *msg, size_t size, int flags)
 | 
								       struct msghdr *msg, size_t size, int flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sock *sk = sock->sk;
 | 
						struct sock *sk = sock->sk;
 | 
				
			||||||
 | 
						struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
 | 
				
			||||||
	int err = 0;
 | 
						int err = 0;
 | 
				
			||||||
	size_t target, copied = 0;
 | 
						size_t target, copied = 0;
 | 
				
			||||||
	long timeo;
 | 
						long timeo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
 | 
				
			||||||
 | 
							rfcomm_dlc_accept(d);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & MSG_OOB)
 | 
						if (flags & MSG_OOB)
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -746,6 +757,7 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sock *sk = sock->sk;
 | 
						struct sock *sk = sock->sk;
 | 
				
			||||||
	int err = 0;
 | 
						int err = 0;
 | 
				
			||||||
 | 
						u32 opt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	BT_DBG("sk %p", sk);
 | 
						BT_DBG("sk %p", sk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -755,6 +767,20 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
 | 
				
			||||||
	lock_sock(sk);
 | 
						lock_sock(sk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (optname) {
 | 
						switch (optname) {
 | 
				
			||||||
 | 
						case BT_DEFER_SETUP:
 | 
				
			||||||
 | 
							if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
 | 
				
			||||||
 | 
								err = -EINVAL;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (get_user(opt, (u32 __user *) optval)) {
 | 
				
			||||||
 | 
								err = -EFAULT;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bt_sk(sk)->defer_setup = opt;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		err = -ENOPROTOOPT;
 | 
							err = -ENOPROTOOPT;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					@ -785,7 +811,8 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case RFCOMM_CONNINFO:
 | 
						case RFCOMM_CONNINFO:
 | 
				
			||||||
		if (sk->sk_state != BT_CONNECTED) {
 | 
							if (sk->sk_state != BT_CONNECTED &&
 | 
				
			||||||
 | 
										!rfcomm_pi(sk)->dlc->defer_setup) {
 | 
				
			||||||
			err = -ENOTCONN;
 | 
								err = -ENOTCONN;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -826,6 +853,17 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
 | 
				
			||||||
	lock_sock(sk);
 | 
						lock_sock(sk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (optname) {
 | 
						switch (optname) {
 | 
				
			||||||
 | 
						case BT_DEFER_SETUP:
 | 
				
			||||||
 | 
							if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
 | 
				
			||||||
 | 
								err = -EINVAL;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval))
 | 
				
			||||||
 | 
								err = -EFAULT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		err = -ENOPROTOOPT;
 | 
							err = -ENOPROTOOPT;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
| 
						 | 
					@ -938,6 +976,10 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
done:
 | 
					done:
 | 
				
			||||||
	bh_unlock_sock(parent);
 | 
						bh_unlock_sock(parent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bt_sk(parent)->defer_setup)
 | 
				
			||||||
 | 
							parent->sk_state_change(parent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue