diff options
Diffstat (limited to 'net/bluetooth/l2cap_sock.c')
| -rw-r--r-- | net/bluetooth/l2cap_sock.c | 130 | 
1 files changed, 119 insertions, 11 deletions
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 3bb1611b9d4..a4bb27e8427 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -27,7 +27,6 @@  /* Bluetooth L2CAP sockets. */ -#include <linux/security.h>  #include <linux/export.h>  #include <net/bluetooth/bluetooth.h> @@ -89,8 +88,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)  	if (err < 0)  		goto done; -	if (__le16_to_cpu(la.l2_psm) == 0x0001 || -				__le16_to_cpu(la.l2_psm) == 0x0003) +	if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP || +	    __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM)  		chan->sec_level = BT_SECURITY_SDP;  	bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); @@ -446,6 +445,22 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch  	return err;  } +static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu) +{ +	switch (chan->scid) { +	case L2CAP_CID_LE_DATA: +		if (mtu < L2CAP_LE_MIN_MTU) +			return false; +		break; + +	default: +		if (mtu < L2CAP_DEFAULT_MIN_MTU) +			return false; +	} + +	return true; +} +  static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen)  {  	struct sock *sk = sock->sk; @@ -484,6 +499,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us  			break;  		} +		if (!l2cap_valid_mtu(chan, opts.imtu)) { +			err = -EINVAL; +			break; +		} +  		chan->mode = opts.mode;  		switch (chan->mode) {  		case L2CAP_MODE_BASIC: @@ -873,9 +893,34 @@ static int l2cap_sock_release(struct socket *sock)  	return err;  } -static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) +static void l2cap_sock_cleanup_listen(struct sock *parent)  { -	struct sock *sk, *parent = data; +	struct sock *sk; + +	BT_DBG("parent %p", parent); + +	/* Close not yet accepted channels */ +	while ((sk = bt_accept_dequeue(parent, NULL))) { +		struct l2cap_chan *chan = l2cap_pi(sk)->chan; + +		l2cap_chan_lock(chan); +		__clear_chan_timer(chan); +		l2cap_chan_close(chan, ECONNRESET); +		l2cap_chan_unlock(chan); + +		l2cap_sock_kill(sk); +	} +} + +static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) +{ +	struct sock *sk, *parent = chan->data; + +	/* Check for backlog size */ +	if (sk_acceptq_is_full(parent)) { +		BT_DBG("backlog full %d", parent->sk_ack_backlog); +		return NULL; +	}  	sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,  								GFP_ATOMIC); @@ -889,10 +934,10 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data)  	return l2cap_pi(sk)->chan;  } -static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) +static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)  {  	int err; -	struct sock *sk = data; +	struct sock *sk = chan->data;  	struct l2cap_pinfo *pi = l2cap_pi(sk);  	lock_sock(sk); @@ -925,16 +970,57 @@ done:  	return err;  } -static void l2cap_sock_close_cb(void *data) +static void l2cap_sock_close_cb(struct l2cap_chan *chan)  { -	struct sock *sk = data; +	struct sock *sk = chan->data;  	l2cap_sock_kill(sk);  } -static void l2cap_sock_state_change_cb(void *data, int state) +static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)  { -	struct sock *sk = data; +	struct sock *sk = chan->data; +	struct sock *parent; + +	lock_sock(sk); + +	parent = bt_sk(sk)->parent; + +	sock_set_flag(sk, SOCK_ZAPPED); + +	switch (chan->state) { +	case BT_OPEN: +	case BT_BOUND: +	case BT_CLOSED: +		break; +	case BT_LISTEN: +		l2cap_sock_cleanup_listen(sk); +		sk->sk_state = BT_CLOSED; +		chan->state = BT_CLOSED; + +		break; +	default: +		sk->sk_state = BT_CLOSED; +		chan->state = BT_CLOSED; + +		sk->sk_err = err; + +		if (parent) { +			bt_accept_unlink(sk); +			parent->sk_data_ready(parent, 0); +		} else { +			sk->sk_state_change(sk); +		} + +		break; +	} + +	release_sock(sk); +} + +static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state) +{ +	struct sock *sk = chan->data;  	sk->sk_state = state;  } @@ -955,12 +1041,34 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,  	return skb;  } +static void l2cap_sock_ready_cb(struct l2cap_chan *chan) +{ +	struct sock *sk = chan->data; +	struct sock *parent; + +	lock_sock(sk); + +	parent = bt_sk(sk)->parent; + +	BT_DBG("sk %p, parent %p", sk, parent); + +	sk->sk_state = BT_CONNECTED; +	sk->sk_state_change(sk); + +	if (parent) +		parent->sk_data_ready(parent, 0); + +	release_sock(sk); +} +  static struct l2cap_ops l2cap_chan_ops = {  	.name		= "L2CAP Socket Interface",  	.new_connection	= l2cap_sock_new_connection_cb,  	.recv		= l2cap_sock_recv_cb,  	.close		= l2cap_sock_close_cb, +	.teardown	= l2cap_sock_teardown_cb,  	.state_change	= l2cap_sock_state_change_cb, +	.ready		= l2cap_sock_ready_cb,  	.alloc_skb	= l2cap_sock_alloc_skb_cb,  };  |