From 224f8af0db1cd50e9d954ca9d5a7d7716f63d2b0 Mon Sep 17 00:00:00 2001 From: Ruiyi Zhang Date: Fri, 13 May 2011 13:07:52 +0800 Subject: Bluetooth: Allow unsegmented SDU retries on sock_queue_rcv_skb failure In L2CAP_SDU_UNSEGMENTED case, if sock_queue_rcv_skb returns error, l2cap_ertm_reassembly_sdu should not return 0 so as to insert the skb into BUSY_QUEUE for later retries. Signed-off-by: Ruiyi Zhang Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e64a1c2..6b59005 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2928,11 +2928,7 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk if (chan->conn_state & L2CAP_CONN_SAR_SDU) goto drop; - err = sock_queue_rcv_skb(chan->sk, skb); - if (!err) - return err; - - break; + return sock_queue_rcv_skb(chan->sk, skb); case L2CAP_SDU_START: if (chan->conn_state & L2CAP_CONN_SAR_SDU) -- cgit v1.1 From 78676a06260a4162b6837a8a2fb954445004ebc2 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 18 May 2011 18:14:45 -0300 Subject: Bluetooth: fix set but not used warning Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6b59005..af1bc30 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3663,7 +3663,6 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk { struct l2cap_chan *chan; struct sock *sk = NULL; - struct l2cap_pinfo *pi; u16 control; u8 tx_seq; int len; @@ -3675,7 +3674,6 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk } sk = chan->sk; - pi = l2cap_pi(sk); BT_DBG("chan %p, len %d", chan, skb->len); -- cgit v1.1 From 75fde9ee1bca0c0c004aadaebdedf17370f93f18 Mon Sep 17 00:00:00 2001 From: David Miller Date: Thu, 19 May 2011 18:09:00 -0400 Subject: Bluetooth: Kill set but not used variable 'l2cap_sk' in rfcomm_sock_getsockopt_old() Signed-off-by: David S. Miller Signed-off-by: Gustavo F. Padovan --- net/bluetooth/rfcomm/sock.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 386cfaf..bcf077c 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -741,7 +741,6 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; - struct sock *l2cap_sk; struct rfcomm_conninfo cinfo; struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; int len, err = 0; @@ -786,8 +785,6 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u break; } - l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk; - cinfo.hci_handle = conn->hcon->handle; memcpy(cinfo.dev_class, conn->hcon->dev_class, 3); -- cgit v1.1 From 37e1c55de7b1edd3fb8fc3411ad0c32a213723d0 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 20 May 2011 11:50:41 -0700 Subject: Bluetooth: Remove unnecessary use of hci_dev_list_lock The get_connections function has no need to use hci_dev_list_lock. The code was there probably because of a copy-paste mistake. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index dae382c..fe835b8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1092,8 +1092,6 @@ static int get_connections(struct sock *sk, u16 index) put_unaligned_le16(count, &rp->conn_count); - read_lock(&hci_dev_list_lock); - i = 0; list_for_each(p, &hdev->conn_hash.list) { struct hci_conn *c = list_entry(p, struct hci_conn, list); @@ -1101,8 +1099,6 @@ static int get_connections(struct sock *sk, u16 index) bacpy(&rp->conn[i++], &c->dst); } - read_unlock(&hci_dev_list_lock); - err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len); unlock: -- cgit v1.1 From 4519de9a0478d8de438f8b80ab2e94668ef63ab4 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 28 Apr 2011 17:55:53 -0300 Subject: Bluetooth: Create __l2cap_chan_close() This is actually __l2cap_sock_close() renamed to __l2cap_chan_close(). At a first look it may not make sense, but with the further cleanups that will come it will. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 97 +++++++++++++++++++++++++++++++++++++++++++--- net/bluetooth/l2cap_sock.c | 85 +--------------------------------------- 2 files changed, 93 insertions(+), 89 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index af1bc30..14c760c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -69,7 +69,11 @@ static void l2cap_busy_work(struct work_struct *work); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); +static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, + void *data); static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data); +static void l2cap_send_disconn_req(struct l2cap_conn *conn, + struct l2cap_chan *chan, int err); static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb); @@ -271,7 +275,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) /* Delete channel. * Must be called on the locked socket. */ -void l2cap_chan_del(struct l2cap_chan *chan, int err) +static void l2cap_chan_del(struct l2cap_chan *chan, int err) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -327,6 +331,87 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) } } +/* Must be called on unlocked socket. */ +static void l2cap_chan_close(struct sock *sk) +{ + l2cap_sock_clear_timer(sk); + lock_sock(sk); + __l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); + release_sock(sk); + l2cap_sock_kill(sk); +} + +static void l2cap_chan_cleanup_listen(struct sock *parent) +{ + struct sock *sk; + + BT_DBG("parent %p", parent); + + /* Close not yet accepted channels */ + while ((sk = bt_accept_dequeue(parent, NULL))) + l2cap_chan_close(sk); + + parent->sk_state = BT_CLOSED; + sock_set_flag(parent, SOCK_ZAPPED); +} + +void __l2cap_chan_close(struct l2cap_chan *chan, int reason) +{ + struct l2cap_conn *conn = chan->conn; + struct sock *sk = chan->sk; + + BT_DBG("chan %p state %d socket %p", chan, sk->sk_state, sk->sk_socket); + + switch (sk->sk_state) { + case BT_LISTEN: + l2cap_chan_cleanup_listen(sk); + break; + + case BT_CONNECTED: + case BT_CONFIG: + if ((sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) && + conn->hcon->type == ACL_LINK) { + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_send_disconn_req(conn, chan, reason); + } else + l2cap_chan_del(chan, reason); + break; + + case BT_CONNECT2: + if ((sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) && + conn->hcon->type == ACL_LINK) { + struct l2cap_conn_rsp rsp; + __u16 result; + + if (bt_sk(sk)->defer_setup) + result = L2CAP_CR_SEC_BLOCK; + else + result = L2CAP_CR_BAD_PSM; + + rsp.scid = cpu_to_le16(chan->dcid); + rsp.dcid = cpu_to_le16(chan->scid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, + sizeof(rsp), &rsp); + } + + l2cap_chan_del(chan, reason); + break; + + case BT_CONNECT: + case BT_DISCONN: + l2cap_chan_del(chan, reason); + break; + + default: + sock_set_flag(sk, SOCK_ZAPPED); + break; + } +} + static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) { struct sock *sk = chan->sk; @@ -393,7 +478,7 @@ u8 l2cap_get_ident(struct l2cap_conn *conn) return id; } -void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) +static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) { struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); u8 flags; @@ -533,7 +618,7 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) } } -void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err) +static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err) { struct sock *sk; struct l2cap_disconn_req req; @@ -591,10 +676,10 @@ static void l2cap_conn_start(struct l2cap_conn *conn) conn->feat_mask) && chan->conf_state & L2CAP_CONF_STATE2_DEVICE) { - /* __l2cap_sock_close() calls list_del(chan) + /* __l2cap_chan_close() calls list_del(chan) * so release the lock */ read_unlock_bh(&conn->chan_lock); - __l2cap_sock_close(sk, ECONNRESET); + __l2cap_chan_close(chan, ECONNRESET); read_lock_bh(&conn->chan_lock); bh_unlock_sock(sk); continue; @@ -3943,7 +4028,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) l2cap_sock_clear_timer(sk); l2cap_sock_set_timer(sk, HZ * 5); } else if (chan->sec_level == BT_SECURITY_HIGH) - __l2cap_sock_close(sk, ECONNREFUSED); + __l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) l2cap_sock_clear_timer(sk); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 18dc988..290130c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -58,7 +58,7 @@ static void l2cap_sock_timeout(unsigned long arg) else reason = ETIMEDOUT; - __l2cap_sock_close(sk, reason); + __l2cap_chan_close(l2cap_pi(sk)->chan, reason); bh_unlock_sock(sk); @@ -813,87 +813,6 @@ void l2cap_sock_kill(struct sock *sk) sock_put(sk); } -/* Must be called on unlocked socket. */ -static void l2cap_sock_close(struct sock *sk) -{ - l2cap_sock_clear_timer(sk); - lock_sock(sk); - __l2cap_sock_close(sk, ECONNRESET); - release_sock(sk); - l2cap_sock_kill(sk); -} - -static void l2cap_sock_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) - l2cap_sock_close(sk); - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); -} - -void __l2cap_sock_close(struct sock *sk, int reason) -{ - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct l2cap_conn *conn = chan->conn; - - BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); - - switch (sk->sk_state) { - case BT_LISTEN: - l2cap_sock_cleanup_listen(sk); - break; - - case BT_CONNECTED: - case BT_CONFIG: - if ((sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) && - conn->hcon->type == ACL_LINK) { - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, chan, reason); - } else - l2cap_chan_del(chan, reason); - break; - - case BT_CONNECT2: - if ((sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) && - conn->hcon->type == ACL_LINK) { - struct l2cap_conn_rsp rsp; - __u16 result; - - if (bt_sk(sk)->defer_setup) - result = L2CAP_CR_SEC_BLOCK; - else - result = L2CAP_CR_BAD_PSM; - - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); - } - - l2cap_chan_del(chan, reason); - break; - - case BT_CONNECT: - case BT_DISCONN: - l2cap_chan_del(chan, reason); - break; - - default: - sock_set_flag(sk, SOCK_ZAPPED); - break; - } -} - static int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; @@ -912,7 +831,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) sk->sk_shutdown = SHUTDOWN_MASK; l2cap_sock_clear_timer(sk); - __l2cap_sock_close(sk, 0); + __l2cap_chan_close(chan, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, -- cgit v1.1 From 9a91a04a95d30a18909e2aec9d7b17b4c86088a7 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 28 Apr 2011 18:50:17 -0300 Subject: Bluetooth: Create l2cap_chan_send() This move all the sending logic to l2cap_core.c, but we still have a socket dependence there, struct msghdr. It will be removed in some of the further commits. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 80 ++++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/l2cap_sock.c | 83 ++-------------------------------------------- 2 files changed, 83 insertions(+), 80 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 14c760c..e65f631 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1535,6 +1535,86 @@ int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t le return size; } +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +{ + struct sock *sk = chan->sk; + struct sk_buff *skb; + u16 control; + int err; + + /* Connectionless channel */ + if (sk->sk_type == SOCK_DGRAM) { + skb = l2cap_create_connless_pdu(chan, msg, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + l2cap_do_send(chan, skb); + return len; + } + + switch (chan->mode) { + case L2CAP_MODE_BASIC: + /* Check outgoing MTU */ + if (len > chan->omtu) + return -EMSGSIZE; + + /* Create a basic PDU */ + skb = l2cap_create_basic_pdu(chan, msg, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + l2cap_do_send(chan, skb); + err = len; + break; + + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + /* Entire SDU fits into one PDU */ + if (len <= chan->remote_mps) { + control = L2CAP_SDU_UNSEGMENTED; + skb = l2cap_create_iframe_pdu(chan, msg, len, control, + 0); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + __skb_queue_tail(&chan->tx_q, skb); + + if (chan->tx_send_head == NULL) + chan->tx_send_head = skb; + + } else { + /* Segment SDU into multiples PDUs */ + err = l2cap_sar_segment_sdu(chan, msg, len); + if (err < 0) + return err; + } + + if (chan->mode == L2CAP_MODE_STREAMING) { + l2cap_streaming_send(chan); + err = len; + break; + } + + if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (chan->conn_state & L2CAP_CONN_WAIT_F)) { + err = len; + break; + } + + err = l2cap_ertm_send(chan); + if (err >= 0) + err = len; + + break; + + default: + BT_DBG("bad state %1.1x", chan->mode); + err = -EBADFD; + } + + return err; +} + static void l2cap_chan_ready(struct sock *sk) { struct sock *parent = bt_sk(sk)->parent; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 290130c..0ecf214 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -673,8 +673,6 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct sk_buff *skb; - u16 control; int err; BT_DBG("sock %p, sk %p", sock, sk); @@ -689,87 +687,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms lock_sock(sk); if (sk->sk_state != BT_CONNECTED) { - err = -ENOTCONN; - goto done; + release_sock(sk); + return -ENOTCONN; } - /* Connectionless channel */ - if (sk->sk_type == SOCK_DGRAM) { - skb = l2cap_create_connless_pdu(chan, msg, len); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - } else { - l2cap_do_send(chan, skb); - err = len; - } - goto done; - } + err = l2cap_chan_send(chan, msg, len); - switch (chan->mode) { - case L2CAP_MODE_BASIC: - /* Check outgoing MTU */ - if (len > chan->omtu) { - err = -EMSGSIZE; - goto done; - } - - /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(chan, msg, len); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - goto done; - } - - l2cap_do_send(chan, skb); - err = len; - break; - - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - /* Entire SDU fits into one PDU */ - if (len <= chan->remote_mps) { - control = L2CAP_SDU_UNSEGMENTED; - skb = l2cap_create_iframe_pdu(chan, msg, len, control, - 0); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - goto done; - } - __skb_queue_tail(&chan->tx_q, skb); - - if (chan->tx_send_head == NULL) - chan->tx_send_head = skb; - - } else { - /* Segment SDU into multiples PDUs */ - err = l2cap_sar_segment_sdu(chan, msg, len); - if (err < 0) - goto done; - } - - if (chan->mode == L2CAP_MODE_STREAMING) { - l2cap_streaming_send(chan); - err = len; - break; - } - - if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (chan->conn_state & L2CAP_CONN_WAIT_F)) { - err = len; - break; - } - err = l2cap_ertm_send(chan); - - if (err >= 0) - err = len; - break; - - default: - BT_DBG("bad state %1.1x", chan->mode); - err = -EBADFD; - } - -done: release_sock(sk); return err; } -- cgit v1.1 From 715ec005cd10c5d53109ffe513e4d403644e3e48 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 2 May 2011 17:13:55 -0300 Subject: Bluetooth: Add chan->chan_type struct member chan_type says if our chan is raw(direclty access to HCI), connection less or connection oriented. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 30 +++++++++++------------------- net/bluetooth/l2cap_sock.c | 29 ++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e65f631..f5e50bf 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -245,7 +245,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->conn = conn; - if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) { + if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) { if (conn->hcon->type == LE_LINK) { /* LE connection */ chan->omtu = L2CAP_LE_DEFAULT_MTU; @@ -256,7 +256,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->scid = l2cap_alloc_cid(conn); chan->omtu = L2CAP_DEFAULT_MTU; } - } else if (sk->sk_type == SOCK_DGRAM) { + } else if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { /* Connectionless socket */ chan->scid = L2CAP_CID_CONN_LESS; chan->dcid = L2CAP_CID_CONN_LESS; @@ -369,8 +369,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONNECTED: case BT_CONFIG: - if ((sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) && + if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { l2cap_sock_set_timer(sk, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); @@ -379,8 +378,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) break; case BT_CONNECT2: - if ((sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) && + if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { struct l2cap_conn_rsp rsp; __u16 result; @@ -414,9 +412,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) { - struct sock *sk = chan->sk; - - if (sk->sk_type == SOCK_RAW) { + if (chan->chan_type == L2CAP_CHAN_RAW) { switch (chan->sec_level) { case BT_SECURITY_HIGH: return HCI_AT_DEDICATED_BONDING_MITM; @@ -657,8 +653,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) bh_lock_sock(sk); - if (sk->sk_type != SOCK_SEQPACKET && - sk->sk_type != SOCK_STREAM) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { bh_unlock_sock(sk); continue; } @@ -852,8 +847,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) sk->sk_state_change(sk); } - if (sk->sk_type != SOCK_SEQPACKET && - sk->sk_type != SOCK_STREAM) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { l2cap_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); @@ -1056,8 +1050,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan) l2cap_sock_set_timer(sk, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { - if (sk->sk_type != SOCK_SEQPACKET && - sk->sk_type != SOCK_STREAM) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { l2cap_sock_clear_timer(sk); if (l2cap_check_security(chan)) sk->sk_state = BT_CONNECTED; @@ -1537,13 +1530,12 @@ int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t le int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { - struct sock *sk = chan->sk; struct sk_buff *skb; u16 control; int err; /* Connectionless channel */ - if (sk->sk_type == SOCK_DGRAM) { + if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { skb = l2cap_create_connless_pdu(chan, msg, len); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1650,7 +1642,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) read_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { struct sock *sk = chan->sk; - if (sk->sk_type != SOCK_RAW) + if (chan->chan_type != L2CAP_CHAN_RAW) continue; /* Don't send frame to the socket it came from */ @@ -4100,7 +4092,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) { struct sock *sk = chan->sk; - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM) + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) return; if (encrypt == 0x00) { diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 0ecf214..2fcdf5e 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -162,7 +162,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al lock_sock(sk); - if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) + if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(la.l2_psm || la.l2_cid)) { err = -EINVAL; goto done; @@ -204,8 +204,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al } /* PSM must be odd and lsb of upper byte must be 0 */ - if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && - sk->sk_type != SOCK_RAW && !la.l2_cid) { + if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && !la.l2_cid && + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; goto done; } @@ -453,8 +453,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } @@ -599,8 +599,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } @@ -806,6 +806,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) sk->sk_type = parent->sk_type; bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; + chan->chan_type = pchan->chan_type; chan->imtu = pchan->imtu; chan->omtu = pchan->omtu; chan->conf_state = pchan->conf_state; @@ -818,6 +819,20 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->force_reliable = pchan->force_reliable; chan->flushable = pchan->flushable; } else { + + switch (sk->sk_type) { + case SOCK_RAW: + chan->chan_type = L2CAP_CHAN_RAW; + break; + case SOCK_DGRAM: + chan->chan_type = L2CAP_CHAN_CONN_LESS; + break; + case SOCK_SEQPACKET: + case SOCK_STREAM: + chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; + break; + } + chan->imtu = L2CAP_DEFAULT_MTU; chan->omtu = 0; if (!disable_ertm && sk->sk_type == SOCK_STREAM) { -- cgit v1.1 From ab07801d28985090ac38047b5a4d8952a7e1689f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 2 May 2011 18:25:01 -0300 Subject: Bluetooth: create channel timer to replace sk_timer The new timer does not belong to struct sock, tought it still touch some sock things, but this will be sorted out soon. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 100 +++++++++++++++++++++++++++++++++------------ net/bluetooth/l2cap_sock.c | 50 +---------------------- 2 files changed, 76 insertions(+), 74 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f5e50bf..fce48f9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -208,6 +208,56 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) return 0; } +static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) +{ + BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->sk->sk_state, + timeout); + if (!mod_timer(&chan->chan_timer, jiffies + timeout)) + sock_hold(chan->sk); +} + +void l2cap_chan_clear_timer(struct l2cap_chan *chan) +{ + BT_DBG("chan %p state %d", chan, chan->sk->sk_state); + + if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer)) + __sock_put(chan->sk); +} + +static void l2cap_chan_timeout(unsigned long arg) +{ + struct l2cap_chan *chan = (struct l2cap_chan *) arg; + struct sock *sk = chan->sk; + int reason; + + BT_DBG("chan %p state %d", chan, sk->sk_state); + + bh_lock_sock(sk); + + if (sock_owned_by_user(sk)) { + /* sk is owned by user. Try again later */ + l2cap_chan_set_timer(chan, HZ / 5); + bh_unlock_sock(sk); + sock_put(sk); + return; + } + + if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) + reason = ECONNREFUSED; + else if (sk->sk_state == BT_CONNECT && + chan->sec_level != BT_SECURITY_SDP) + reason = ECONNREFUSED; + else + reason = ETIMEDOUT; + + __l2cap_chan_close(chan, reason); + + bh_unlock_sock(sk); + + l2cap_sock_kill(sk); + sock_put(sk); +} + struct l2cap_chan *l2cap_chan_create(struct sock *sk) { struct l2cap_chan *chan; @@ -222,6 +272,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) list_add(&chan->global_l, &chan_list); write_unlock_bh(&chan_list_lock); + setup_timer(&chan->chan_timer, l2cap_chan_timeout, (unsigned long) chan); + return chan; } @@ -281,7 +333,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) struct l2cap_conn *conn = chan->conn; struct sock *parent = bt_sk(sk)->parent; - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); BT_DBG("chan %p, conn %p, err %d", chan, conn, err); @@ -334,7 +386,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) /* Must be called on unlocked socket. */ static void l2cap_chan_close(struct sock *sk) { - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(l2cap_pi(sk)->chan); lock_sock(sk); __l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); release_sock(sk); @@ -371,7 +423,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_chan_set_timer(chan, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); } else l2cap_chan_del(chan, reason); @@ -814,7 +866,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) __l2cap_chan_add(conn, chan); - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_chan_set_timer(chan, sk->sk_sndtimeo); sk->sk_state = BT_CONNECTED; parent->sk_data_ready(parent, 0); @@ -842,13 +894,13 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) bh_lock_sock(sk); if (conn->hcon->type == LE_LINK) { - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); } if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); } else if (sk->sk_state == BT_CONNECT) @@ -1047,11 +1099,11 @@ int l2cap_chan_connect(struct l2cap_chan *chan) l2cap_chan_add(conn, chan); sk->sk_state = BT_CONNECT; - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_chan_set_timer(chan, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); if (l2cap_check_security(chan)) sk->sk_state = BT_CONNECTED; } else @@ -1615,7 +1667,7 @@ static void l2cap_chan_ready(struct sock *sk) BT_DBG("sk %p, parent %p", sk, parent); chan->conf_state = 0; - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); if (!parent) { /* Outgoing channel. @@ -2317,7 +2369,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd dcid = chan->scid; - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_chan_set_timer(chan, sk->sk_sndtimeo); chan->ident = cmd->ident; @@ -2434,8 +2486,8 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { sk->sk_state = BT_DISCONN; - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 5); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ / 5); break; } @@ -2608,7 +2660,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr default: sk->sk_err = ECONNRESET; - l2cap_sock_set_timer(sk, HZ * 5); + l2cap_chan_set_timer(chan, HZ * 5); l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } @@ -2664,8 +2716,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { sk->sk_state = BT_DISCONN; - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 5); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); return 0; } @@ -2698,8 +2750,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { sk->sk_state = BT_DISCONN; - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 5); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); return 0; } @@ -4090,20 +4142,18 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) { - struct sock *sk = chan->sk; - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) return; if (encrypt == 0x00) { if (chan->sec_level == BT_SECURITY_MEDIUM) { - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ * 5); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ * 5); } else if (chan->sec_level == BT_SECURITY_HIGH) __l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); } } @@ -4148,8 +4198,8 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } else { - l2cap_sock_clear_timer(sk); - l2cap_sock_set_timer(sk, HZ / 10); + l2cap_chan_clear_timer(chan); + l2cap_chan_set_timer(chan, HZ / 10); } } else if (sk->sk_state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; @@ -4160,7 +4210,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) result = L2CAP_CR_SUCCESS; } else { sk->sk_state = BT_DISCONN; - l2cap_sock_set_timer(sk, HZ / 10); + l2cap_chan_set_timer(chan, HZ / 10); result = L2CAP_CR_SEC_BLOCK; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 2fcdf5e..89433a4 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -32,52 +32,6 @@ static const struct proto_ops l2cap_sock_ops; -/* ---- L2CAP timers ---- */ -static void l2cap_sock_timeout(unsigned long arg) -{ - struct sock *sk = (struct sock *) arg; - int reason; - - BT_DBG("sock %p state %d", sk, sk->sk_state); - - bh_lock_sock(sk); - - if (sock_owned_by_user(sk)) { - /* sk is owned by user. Try again later */ - l2cap_sock_set_timer(sk, HZ / 5); - bh_unlock_sock(sk); - sock_put(sk); - return; - } - - if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) - reason = ECONNREFUSED; - else if (sk->sk_state == BT_CONNECT && - l2cap_pi(sk)->chan->sec_level != BT_SECURITY_SDP) - reason = ECONNREFUSED; - else - reason = ETIMEDOUT; - - __l2cap_chan_close(l2cap_pi(sk)->chan, reason); - - bh_unlock_sock(sk); - - l2cap_sock_kill(sk); - sock_put(sk); -} - -void l2cap_sock_set_timer(struct sock *sk, long timeout) -{ - BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout); - sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); -} - -void l2cap_sock_clear_timer(struct sock *sk) -{ - BT_DBG("sock %p state %d", sk, sk->sk_state); - sk_stop_timer(sk, &sk->sk_timer); -} - static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { struct sock *sk = sock->sk; @@ -753,7 +707,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) err = __l2cap_wait_ack(sk); sk->sk_shutdown = SHUTDOWN_MASK; - l2cap_sock_clear_timer(sk); + l2cap_chan_clear_timer(chan); __l2cap_chan_close(chan, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) @@ -879,8 +833,6 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g sk->sk_protocol = proto; sk->sk_state = BT_OPEN; - setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk); - return sk; } -- cgit v1.1 From 500698d3fd987f6c405d6d5f68fdf535a539e71e Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 4 May 2011 19:35:27 -0300 Subject: Bluetooth: Remove export of l2cap_chan_clear_timer() The call to l2cap_chan_clear_timer() is not really needed in l2cap_sock.c. This patch also adds a call to l2cap_chan_clear_timer() to the only place in __l2cap_sock_close() that wasn't calling it. It's safe call it there because l2cap_chan_clear_timer() check first for timer_peding(). Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 3 ++- net/bluetooth/l2cap_sock.c | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fce48f9..248f68a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -216,7 +216,7 @@ static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) sock_hold(chan->sk); } -void l2cap_chan_clear_timer(struct l2cap_chan *chan) +static void l2cap_chan_clear_timer(struct l2cap_chan *chan) { BT_DBG("chan %p state %d", chan, chan->sk->sk_state); @@ -423,6 +423,7 @@ void __l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { + l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); } else diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 89433a4..7d87d1f 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -707,7 +707,6 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) err = __l2cap_wait_ack(sk); sk->sk_shutdown = SHUTDOWN_MASK; - l2cap_chan_clear_timer(chan); __l2cap_chan_close(chan, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) -- cgit v1.1 From 0f8527249646bbe75d036fe8b9b84d662ece90a9 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 4 May 2011 19:42:50 -0300 Subject: Bluetooth: Rename __l2cap_chan_close() to l2cap_chan_close() To make it consistent with the rest of the API. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 29 ++++++++++++----------------- net/bluetooth/l2cap_sock.c | 2 +- 2 files changed, 13 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 248f68a..c630a0e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -250,7 +250,7 @@ static void l2cap_chan_timeout(unsigned long arg) else reason = ETIMEDOUT; - __l2cap_chan_close(chan, reason); + l2cap_chan_close(chan, reason); bh_unlock_sock(sk); @@ -383,16 +383,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) } } -/* Must be called on unlocked socket. */ -static void l2cap_chan_close(struct sock *sk) -{ - l2cap_chan_clear_timer(l2cap_pi(sk)->chan); - lock_sock(sk); - __l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); - release_sock(sk); - l2cap_sock_kill(sk); -} - static void l2cap_chan_cleanup_listen(struct sock *parent) { struct sock *sk; @@ -400,14 +390,19 @@ static void l2cap_chan_cleanup_listen(struct sock *parent) BT_DBG("parent %p", parent); /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) - l2cap_chan_close(sk); + while ((sk = bt_accept_dequeue(parent, NULL))) { + l2cap_chan_clear_timer(l2cap_pi(sk)->chan); + lock_sock(sk); + l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); + release_sock(sk); + l2cap_sock_kill(sk); + } parent->sk_state = BT_CLOSED; sock_set_flag(parent, SOCK_ZAPPED); } -void __l2cap_chan_close(struct l2cap_chan *chan, int reason) +void l2cap_chan_close(struct l2cap_chan *chan, int reason) { struct l2cap_conn *conn = chan->conn; struct sock *sk = chan->sk; @@ -724,10 +719,10 @@ static void l2cap_conn_start(struct l2cap_conn *conn) conn->feat_mask) && chan->conf_state & L2CAP_CONF_STATE2_DEVICE) { - /* __l2cap_chan_close() calls list_del(chan) + /* l2cap_chan_close() calls list_del(chan) * so release the lock */ read_unlock_bh(&conn->chan_lock); - __l2cap_chan_close(chan, ECONNRESET); + l2cap_chan_close(chan, ECONNRESET); read_lock_bh(&conn->chan_lock); bh_unlock_sock(sk); continue; @@ -4151,7 +4146,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ * 5); } else if (chan->sec_level == BT_SECURITY_HIGH) - __l2cap_chan_close(chan, ECONNREFUSED); + l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) l2cap_chan_clear_timer(chan); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 7d87d1f..b79fb75 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -707,7 +707,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) err = __l2cap_wait_ack(sk); sk->sk_shutdown = SHUTDOWN_MASK; - __l2cap_chan_close(chan, 0); + l2cap_chan_close(chan, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, -- cgit v1.1 From 76c8686f8871f1bcb2dc8b4c5311cd0e2f73d4cd Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 26 May 2011 16:23:50 -0300 Subject: Bluetooth: LE advertising cache This patch implements the LE advertising cache. It stores sensitive information (bdaddr and bdaddr_type so far) gathered from LE advertising report events. Only advertising entries from connectables devices are added to the cache. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 815269b..cc40f22 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1202,6 +1202,67 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, return 0; } +int hci_adv_entries_clear(struct hci_dev *hdev) +{ + struct adv_entry *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &hdev->adv_entries, list) { + list_del(&entry->list); + kfree(entry); + } + + BT_DBG("%s adv cache cleared", hdev->name); + + return 0; +} + +struct adv_entry *hci_find_adv_entry(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct adv_entry *entry; + + list_for_each_entry(entry, &hdev->adv_entries, list) + if (bacmp(bdaddr, &entry->bdaddr) == 0) + return entry; + + return NULL; +} + +static inline int is_connectable_adv(u8 evt_type) +{ + if (evt_type == ADV_IND || evt_type == ADV_DIRECT_IND) + return 1; + + return 0; +} + +int hci_add_adv_entry(struct hci_dev *hdev, + struct hci_ev_le_advertising_info *ev) +{ + struct adv_entry *entry; + + if (!is_connectable_adv(ev->evt_type)) + return -EINVAL; + + /* Only new entries should be added to adv_entries. So, if + * bdaddr was found, don't add it. */ + if (hci_find_adv_entry(hdev, &ev->bdaddr)) + return 0; + + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return -ENOMEM; + + bacpy(&entry->bdaddr, &ev->bdaddr); + entry->bdaddr_type = ev->bdaddr_type; + + list_add(&entry->list, &hdev->adv_entries); + + BT_DBG("%s adv entry added: address %s type %u", hdev->name, + batostr(&entry->bdaddr), entry->bdaddr_type); + + return 0; +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1268,6 +1329,8 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->remote_oob_data); + INIT_LIST_HEAD(&hdev->adv_entries); + INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->power_off, hci_power_off); setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); @@ -1348,6 +1411,7 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_uuids_clear(hdev); hci_link_keys_clear(hdev); hci_remote_oob_data_clear(hdev); + hci_adv_entries_clear(hdev); hci_dev_unlock_bh(hdev); __hci_dev_put(hdev); -- cgit v1.1 From 9aa04c9108164e62cf94d6913183817a0d3dbf03 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 26 May 2011 16:23:51 -0300 Subject: Bluetooth: Add Advertising Report Meta Event handler This patch adds a function to handle LE Advertising Report Meta Events. Signed-off-by: Andre Guedes Signed-off-by: Anderson Briglia Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f13ddbf..db74958 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2680,6 +2680,27 @@ unlock: hci_dev_unlock(hdev); } +static inline void hci_le_adv_report_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_le_advertising_info *ev; + u8 num_reports; + + num_reports = skb->data[0]; + ev = (void *) &skb->data[1]; + + hci_dev_lock(hdev); + + hci_add_adv_entry(hdev, ev); + + while (--num_reports) { + ev = (void *) (ev->data + ev->length + 1); + hci_add_adv_entry(hdev, ev); + } + + hci_dev_unlock(hdev); +} + static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_meta *le_ev = (void *) skb->data; @@ -2691,6 +2712,10 @@ static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_le_conn_complete_evt(hdev, skb); break; + case HCI_EV_LE_ADVERTISING_REPORT: + hci_le_adv_report_evt(hdev, skb); + break; + default: break; } -- cgit v1.1 From eb9d91f5ae9c14583c49223e49f7e88f77b84749 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 26 May 2011 16:23:52 -0300 Subject: Bluetooth: Clear advertising cache before scanning The LE advertising cache should be cleared before performing a LE scanning. This will force the cache to contain only fresh advertising entries. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index db74958..056f7b2 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -839,6 +839,29 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, rp->randomizer, rp->status); } +static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_cp_le_set_scan_enable *cp; + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE); + if (!cp) + return; + + hci_dev_lock(hdev); + + if (cp->enable == 0x01) + hci_adv_entries_clear(hdev); + + hci_dev_unlock(hdev); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1814,6 +1837,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_user_confirm_neg_reply(hdev, skb); break; + case HCI_OP_LE_SET_SCAN_ENABLE: + hci_cc_le_set_scan_enable(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; -- cgit v1.1 From 3581508571b513ed2e66d71f9708d6be907460fd Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Thu, 26 May 2011 16:23:53 -0300 Subject: Bluetooth: Advertising entries lifetime This patch adds a timer to clear 'adv_entries' after three minutes. After some amount of time, the advertising entries cached during the last LE scan should be considered expired and they should be removed from the advertising cache. It was chosen a three minutes timeout as an initial attempt. This value might change in future. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 14 ++++++++++++++ net/bluetooth/hci_event.c | 6 +++++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index cc40f22..ff6b784 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1202,6 +1202,17 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, return 0; } +static void hci_clear_adv_cache(unsigned long arg) +{ + struct hci_dev *hdev = (void *) arg; + + hci_dev_lock(hdev); + + hci_adv_entries_clear(hdev); + + hci_dev_unlock(hdev); +} + int hci_adv_entries_clear(struct hci_dev *hdev) { struct adv_entry *entry, *tmp; @@ -1330,6 +1341,8 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->remote_oob_data); INIT_LIST_HEAD(&hdev->adv_entries); + setup_timer(&hdev->adv_timer, hci_clear_adv_cache, + (unsigned long) hdev); INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->power_off, hci_power_off); @@ -1403,6 +1416,7 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_unregister_sysfs(hdev); hci_del_off_timer(hdev); + del_timer(&hdev->adv_timer); destroy_workqueue(hdev->workqueue); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 056f7b2..a90200c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -856,8 +856,12 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, hci_dev_lock(hdev); - if (cp->enable == 0x01) + if (cp->enable == 0x01) { + del_timer(&hdev->adv_timer); hci_adv_entries_clear(hdev); + } else if (cp->enable == 0x00) { + mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT); + } hci_dev_unlock(hdev); } -- cgit v1.1 From 19f8def031bfa50c579149b200bfeeb919727b27 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Tue, 31 May 2011 15:49:25 +0200 Subject: Bluetooth: Fix auth_complete_evt for legacy units Legacy devices don't re-authenticate the link properly if a link key already exists. Thus, don't update sec_level for this case even if hci_auth_complete_evt indicates success. Otherwise the sec_level will not reflect a real security on the link. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 2 ++ net/bluetooth/hci_event.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3163330..e675402 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -548,6 +548,8 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) cp.handle = cpu_to_le16(conn->handle); hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); + if (conn->key_type != 0xff) + set_bit(HCI_CONN_REAUTH_PEND, &conn->pend); } return 0; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a90200c..33120b4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1489,13 +1489,21 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) { - conn->link_mode |= HCI_LM_AUTH; - conn->sec_level = conn->pending_sec_level; + if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) && + test_bit(HCI_CONN_REAUTH_PEND, + &conn->pend)) { + BT_INFO("re-auth of legacy device is not" + "possible."); + } else { + conn->link_mode |= HCI_LM_AUTH; + conn->sec_level = conn->pending_sec_level; + } } else { mgmt_auth_failed(hdev->id, &conn->dst, ev->status); } clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); + clear_bit(HCI_CONN_REAUTH_PEND, &conn->pend); if (conn->state == BT_CONFIG) { if (!ev->status && hdev->ssp_mode > 0 && -- cgit v1.1 From d7556e20ade582a223ca1792e1f8a7bfd7d5d008 Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Tue, 31 May 2011 15:49:26 +0200 Subject: Bluetooth: Refactor hci_auth_complete_evt function Replace if(conn) with if(!conn) checking to avoid too many nested statements Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 83 +++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 33120b4..6fc766e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1487,59 +1487,58 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - if (!ev->status) { - if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) && - test_bit(HCI_CONN_REAUTH_PEND, - &conn->pend)) { - BT_INFO("re-auth of legacy device is not" - "possible."); - } else { - conn->link_mode |= HCI_LM_AUTH; - conn->sec_level = conn->pending_sec_level; - } + if (!conn) + goto unlock; + + if (!ev->status) { + if (!(conn->ssp_mode > 0 && hdev->ssp_mode > 0) && + test_bit(HCI_CONN_REAUTH_PEND, &conn->pend)) { + BT_INFO("re-auth of legacy device is not possible."); } else { - mgmt_auth_failed(hdev->id, &conn->dst, ev->status); + conn->link_mode |= HCI_LM_AUTH; + conn->sec_level = conn->pending_sec_level; } + } else { + mgmt_auth_failed(hdev->id, &conn->dst, ev->status); + } - clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); - clear_bit(HCI_CONN_REAUTH_PEND, &conn->pend); + clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); + clear_bit(HCI_CONN_REAUTH_PEND, &conn->pend); - if (conn->state == BT_CONFIG) { - if (!ev->status && hdev->ssp_mode > 0 && - conn->ssp_mode > 0) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = ev->handle; - cp.encrypt = 0x01; - hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, - sizeof(cp), &cp); - } else { - conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); - hci_conn_put(conn); - } + if (conn->state == BT_CONFIG) { + if (!ev->status && hdev->ssp_mode > 0 && conn->ssp_mode > 0) { + struct hci_cp_set_conn_encrypt cp; + cp.handle = ev->handle; + cp.encrypt = 0x01; + hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), + &cp); } else { - hci_auth_cfm(conn, ev->status); - - hci_conn_hold(conn); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; + conn->state = BT_CONNECTED; + hci_proto_connect_cfm(conn, ev->status); hci_conn_put(conn); } + } else { + hci_auth_cfm(conn, ev->status); - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { - if (!ev->status) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = ev->handle; - cp.encrypt = 0x01; - hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, - sizeof(cp), &cp); - } else { - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); - hci_encrypt_cfm(conn, ev->status, 0x00); - } + hci_conn_hold(conn); + conn->disc_timeout = HCI_DISCONN_TIMEOUT; + hci_conn_put(conn); + } + + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { + if (!ev->status) { + struct hci_cp_set_conn_encrypt cp; + cp.handle = ev->handle; + cp.encrypt = 0x01; + hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), + &cp); + } else { + clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); + hci_encrypt_cfm(conn, ev->status, 0x00); } } +unlock: hci_dev_unlock(hdev); } -- cgit v1.1 From 29b7988a23daf79c15d587ef9e98e64715aa1ea8 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 31 May 2011 14:20:54 -0300 Subject: Bluetooth: Add 'dst_type' field to struct hci_conn This patch adds a new field (dst_type) to the struct hci_conn which holds the type of the destination address (bdaddr_t dst). This approach is needed in order to use the struct hci_conn as an abstraction of LE connections in HCI Layer. For non-LE this field is ignored. This patch also set properly the 'dst_type' field after initializing LE hci_conn structures. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 6fc766e..afee4ac 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1234,10 +1234,12 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) } else { if (!conn) { conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr); - if (conn) + if (conn) { + conn->dst_type = cp->peer_addr_type; conn->out = 1; - else + } else { BT_ERR("No memory for new connection"); + } } } @@ -2694,6 +2696,8 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff hci_dev_unlock(hdev); return; } + + conn->dst_type = ev->bdaddr_type; } if (ev->status) { -- cgit v1.1 From 893d67514aebcfd3ebf17bd212ceea1e2741a443 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 31 May 2011 14:20:55 -0300 Subject: Bluetooth: Remove useless check in hci_connect() There is no need to check the connection's state since hci_conn_add() has just created a new connection and its state has been set properly. Signed-off-by: Andre Guedes Acked-by: Ville Tervo Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e675402..ca283a0 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -453,8 +453,8 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 le = hci_conn_add(hdev, LE_LINK, dst); if (!le) return ERR_PTR(-ENOMEM); - if (le->state == BT_OPEN) - hci_le_connect(le); + + hci_le_connect(le); hci_conn_hold(le); -- cgit v1.1 From eda42b503a3c866d51146549fe46da1f5f64e2c7 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 31 May 2011 14:20:56 -0300 Subject: Bluetooth: Check advertising cache in hci_connect() When connecting to a LE device, we need to check the advertising cache in order to know the address type of that device. If its advertising entry is not found, the connection is not established and hci_connect() returns error. Signed-off-by: Andre Guedes Acked-by: Ville Tervo Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ca283a0..4e38b48 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -447,13 +447,22 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 BT_DBG("%s dst %s", hdev->name, batostr(dst)); if (type == LE_LINK) { + struct adv_entry *entry; + le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); if (le) return ERR_PTR(-EBUSY); + + entry = hci_find_adv_entry(hdev, dst); + if (!entry) + return ERR_PTR(-EHOSTUNREACH); + le = hci_conn_add(hdev, LE_LINK, dst); if (!le) return ERR_PTR(-ENOMEM); + le->dst_type = entry->bdaddr_type; + hci_le_connect(le); hci_conn_hold(le); -- cgit v1.1 From 6d3ce0e7902314ddb330deaf8827205881d7e59f Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 31 May 2011 14:20:57 -0300 Subject: Bluetooth: Set 'peer_addr_type' in hci_le_connect() Set the 'peer_addr_type' field of the LE Create Connection command sent in hci_le_connect(). Signed-off-by: Andre Guedes Acked-by: Ville Tervo Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 4e38b48..ce67d0ff 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -58,6 +58,7 @@ static void hci_le_connect(struct hci_conn *conn) cp.scan_interval = cpu_to_le16(0x0004); cp.scan_window = cpu_to_le16(0x0004); bacpy(&cp.peer_addr, &conn->dst); + cp.peer_addr_type = conn->dst_type; cp.conn_interval_min = cpu_to_le16(0x0008); cp.conn_interval_max = cpu_to_le16(0x0100); cp.supervision_timeout = cpu_to_le16(0x0064); -- cgit v1.1 From b5ad8b7f8c3f3711129d67aeda85ceec690c3cd0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jun 2011 08:54:45 +0200 Subject: Bluetooth: fix sparse & gcc warnings sparse complains about a few things that should be static. Signed-off-by: Johannes Berg Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 6 +++--- net/bluetooth/mgmt.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c630a0e..bb6be63 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -62,8 +62,8 @@ static u8 l2cap_fixed_chan[8] = { 0x02, }; static struct workqueue_struct *_busy_wq; -LIST_HEAD(chan_list); -DEFINE_RWLOCK(chan_list_lock); +static LIST_HEAD(chan_list); +static DEFINE_RWLOCK(chan_list_lock); static void l2cap_busy_work(struct work_struct *work); @@ -500,7 +500,7 @@ static inline int l2cap_check_security(struct l2cap_chan *chan) return hci_conn_security(conn->hcon, chan->sec_level, auth_type); } -u8 l2cap_get_ident(struct l2cap_conn *conn) +static u8 l2cap_get_ident(struct l2cap_conn *conn) { u8 id; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fe835b8..89bc36a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -41,7 +41,7 @@ struct pending_cmd { void *user_data; }; -LIST_HEAD(cmd_list); +static LIST_HEAD(cmd_list); static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) { -- cgit v1.1 From 24718ca5eeb6d36a4a78deb34a2d50e4017bbd4a Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Wed, 1 Jun 2011 17:28:47 +0200 Subject: Bluetooth: Remove a magic number Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 89bc36a..5415423 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1143,7 +1143,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, bacpy(&reply.bdaddr, &cp->bdaddr); reply.pin_len = cp->pin_len; - memcpy(reply.pin_code, cp->pin_code, 16); + memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code)); err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply); if (err < 0) -- cgit v1.1 From 96d97a673d42408c0f960cc54d44be7629343bce Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Wed, 1 Jun 2011 17:28:48 +0200 Subject: Bluetooth: Verify a pin code in pin_code_reply As we cannot relay on a userspace mgmt api implementation we should verify if pin_code_reply in fact contains the secure pin code. If userspace replied with unsecure pincode when secure was required we will send pin_code_neg_reply to the controller. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 53 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5415423..fcccf10 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1108,11 +1108,32 @@ unlock: return err; } +static int send_pin_code_neg_reply(struct sock *sk, u16 index, + struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp) +{ + struct pending_cmd *cmd; + int err; + + cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, cp, + sizeof(*cp)); + if (!cmd) + return -ENOMEM; + + err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr), + &cp->bdaddr); + if (err < 0) + mgmt_pending_remove(cmd); + + return err; +} + static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; + struct hci_conn *conn; struct mgmt_cp_pin_code_reply *cp; + struct mgmt_cp_pin_code_neg_reply ncp; struct hci_cp_pin_code_reply reply; struct pending_cmd *cmd; int err; @@ -1135,6 +1156,25 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, goto failed; } + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (!conn) { + err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENOTCONN); + goto failed; + } + + if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) { + bacpy(&ncp.bdaddr, &cp->bdaddr); + + BT_ERR("PIN code is not 16 bytes long"); + + err = send_pin_code_neg_reply(sk, index, hdev, &ncp); + if (err >= 0) + err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, + EINVAL); + + goto failed; + } + cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len); if (!cmd) { err = -ENOMEM; @@ -1161,7 +1201,6 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, { struct hci_dev *hdev; struct mgmt_cp_pin_code_neg_reply *cp; - struct pending_cmd *cmd; int err; BT_DBG(""); @@ -1185,17 +1224,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, - data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr), - &cp->bdaddr); - if (err < 0) - mgmt_pending_remove(cmd); + err = send_pin_code_neg_reply(sk, index, hdev, cp); failed: hci_dev_unlock(hdev); -- cgit v1.1 From 14b12d0b98f87162b7e9e93dde66d1af97886567 Mon Sep 17 00:00:00 2001 From: Jaikumar Ganesh Date: Mon, 23 May 2011 18:06:04 -0700 Subject: Bluetooth: Add BT_POWER L2CAP socket option. Add BT_POWER socket option used to control the power characteristics of the underlying ACL link. When the remote end has put the link in sniff mode and the host stack wants to send data we need need to explicitly exit sniff mode to work well with certain devices (For example, A2DP on Plantronics Voyager 855). However, this causes problems with HID devices. Hence, moving into active mode when sending data, irrespective of who set the sniff mode has been made as a socket option. By default, we will move into active mode. HID devices can set the L2CAP socket option to prevent this from happening. Currently, this has been implemented for L2CAP sockets. This has been tested with incoming and outgoing L2CAP sockets for HID and A2DP. Based on discussions on linux-bluetooth and patches submitted by Andrei Emeltchenko. Signed-off-by: Jaikumar Ganesh Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 9 ++++++--- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/l2cap_core.c | 5 +++++ net/bluetooth/l2cap_sock.c | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ce67d0ff..0408a93 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -507,7 +507,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 if (acl->state == BT_CONNECTED && (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { acl->power_save = 1; - hci_conn_enter_active_mode(acl); + hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON); if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->pend)) { /* defer SCO setup until mode change completed */ @@ -688,7 +688,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role) EXPORT_SYMBOL(hci_conn_switch_role); /* Enter active mode */ -void hci_conn_enter_active_mode(struct hci_conn *conn) +void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) { struct hci_dev *hdev = conn->hdev; @@ -697,7 +697,10 @@ void hci_conn_enter_active_mode(struct hci_conn *conn) if (test_bit(HCI_RAW, &hdev->flags)) return; - if (conn->mode != HCI_CM_SNIFF || !conn->power_save) + if (conn->mode != HCI_CM_SNIFF) + goto timer; + + if (!conn->power_save && !force_active) goto timer; if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ff6b784..e14e8a1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1969,7 +1969,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev) while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); - hci_conn_enter_active_mode(conn); + hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); hci_send_frame(skb); hdev->acl_last_tx = jiffies; @@ -2108,7 +2108,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) if (conn) { register struct hci_proto *hp; - hci_conn_enter_active_mode(conn); + hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); /* Send to upper protocol */ hp = hci_proto[HCI_PROTO_L2CAP]; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bb6be63..6908a83 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -537,6 +537,8 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, else flags = ACL_START; + bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; + hci_send_acl(conn->hcon, skb, flags); } @@ -590,6 +592,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) else flags = ACL_START; + bt_cb(skb)->force_active = chan->force_active; + hci_send_acl(chan->conn->hcon, skb, flags); } @@ -1215,6 +1219,7 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) else flags = ACL_START; + bt_cb(skb)->force_active = chan->force_active; hci_send_acl(hcon, skb, flags); } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index b79fb75..bec3e04 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -390,6 +390,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; + struct bt_power pwr; int len, err = 0; BT_DBG("sk %p", sk); @@ -438,6 +439,21 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; + case BT_POWER: + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { + err = -EINVAL; + break; + } + + pwr.force_active = chan->force_active; + + len = min_t(unsigned int, len, sizeof(pwr)); + if (copy_to_user(optval, (char *) &pwr, len)) + err = -EFAULT; + + break; + default: err = -ENOPROTOOPT; break; @@ -538,6 +554,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; + struct bt_power pwr; int len, err = 0; u32 opt; @@ -614,6 +631,23 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch chan->flushable = opt; break; + case BT_POWER: + if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && + chan->chan_type != L2CAP_CHAN_RAW) { + err = -EINVAL; + break; + } + + pwr.force_active = BT_POWER_FORCE_ACTIVE_ON; + + len = min_t(unsigned int, sizeof(pwr), optlen); + if (copy_from_user((char *) &pwr, optval, len)) { + err = -EFAULT; + break; + } + chan->force_active = pwr.force_active; + break; + default: err = -ENOPROTOOPT; break; @@ -771,6 +805,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->role_switch = pchan->role_switch; chan->force_reliable = pchan->force_reliable; chan->flushable = pchan->flushable; + chan->force_active = pchan->force_active; } else { switch (sk->sk_type) { @@ -801,6 +836,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->role_switch = 0; chan->force_reliable = 0; chan->flushable = BT_FLUSHABLE_OFF; + chan->force_active = BT_POWER_FORCE_ACTIVE_ON; } /* Default config options */ -- cgit v1.1 From ef4177e2bf92543e422fae154888062376e2283d Mon Sep 17 00:00:00 2001 From: Waldemar Rymarkiewicz Date: Thu, 2 Jun 2011 14:24:52 +0200 Subject: Bluetooth: Simplify hci_conn_accept_secure check If the link key is secure (authenticated or combination 16 digit) the sec_level will be always BT_SECURITY_HIGH. Therefore, instead of checking the link key type simply check the sec_level on the link. Signed-off-by: Waldemar Rymarkiewicz Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 0408a93..37f5a17 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -643,9 +643,7 @@ int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level) if (sec_level != BT_SECURITY_HIGH) return 1; /* Accept if non-secure is required */ - if (conn->key_type == HCI_LK_AUTH_COMBINATION || - (conn->key_type == HCI_LK_COMBINATION && - conn->pin_length == 16)) + if (conn->sec_level == BT_SECURITY_HIGH) return 1; return 0; /* Reject not secure link */ -- cgit v1.1 From d8d69c54fb8fbb7483180b609f99f2e3e539962b Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 3 Jun 2011 16:21:07 -0700 Subject: Bluetooth: Restore accidentally-deleted line When code was moved from l2cap_core.c to l2cap_sock.c in commit 6de0702b5b93da0ef097aa092b4597fbc024ebba, one line was dropped from the old __l2cap_sock_close() implementation. This sk_state change should still be in l2cap_chan_close(). Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6908a83..e3b86fc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -435,6 +435,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) result = L2CAP_CR_SEC_BLOCK; else result = L2CAP_CR_BAD_PSM; + sk->sk_state = BT_DISCONN; rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); -- cgit v1.1 From e694928067b52f569a7df39b7f02efd7b34ab8f4 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Fri, 3 Jun 2011 16:21:10 -0700 Subject: Bluetooth: Fix check for the ERTM local busy state Local busy is encoded in a bitfield, but was not masked out correctly. Signed-off-by: Mat Martineau Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e3b86fc..dff9d76 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3540,7 +3540,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont goto drop; } - if (chan->conn_state == L2CAP_CONN_LOCAL_BUSY) + if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) goto drop; if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { -- cgit v1.1 From d0fad89da9801b3945d2ee7243ecbc75b3da6f09 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 12 May 2011 16:50:09 -0700 Subject: Bluetooth: Silence DEBUG_STRICT_USER_COPY_CHECKS=y warning Enabling DEBUG_STRICT_USER_COPY_CHECKS causes the following warning: In function 'copy_from_user', inlined from 'rfcomm_sock_setsockopt' at net/bluetooth/rfcomm/sock.c:705: arch/x86/include/asm/uaccess_64.h:65: warning: call to 'copy_from_user_overflow' declared with attribute warning: copy_from_user() buffer size is not provably correct presumably due to buf_size being signed causing GCC to fail to see that buf_size can't become negative. Cc: Marcel Holtmann Cc: Gustavo F. Padovan Signed-off-by: Stephen Boyd Signed-off-by: Gustavo F. Padovan --- net/bluetooth/rfcomm/sock.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 1b10727..5dea4d8 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -679,7 +679,8 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c { struct sock *sk = sock->sk; struct bt_security sec; - int len, err = 0; + int err = 0; + size_t len; u32 opt; BT_DBG("sk %p", sk); -- cgit v1.1 From 4a77708b055a71bf769a917283a9eba3dcedb017 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 12 May 2011 11:13:15 +0300 Subject: Bluetooth: fix shutdown on SCO sockets shutdown should wait for SCO link to be properly disconnected before detroying the socket, otherwise an application using the socket may assume link is properly disconnected before it really happens which can be a problem when e.g synchronizing profile switch. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Gustavo F. Padovan --- net/bluetooth/sco.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 42fdffd..cb4fb78 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -369,6 +369,15 @@ static void __sco_sock_close(struct sock *sk) case BT_CONNECTED: case BT_CONFIG: + if (sco_pi(sk)->conn) { + sk->sk_state = BT_DISCONN; + sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT); + hci_conn_put(sco_pi(sk)->conn->hcon); + sco_pi(sk)->conn->hcon = NULL; + } else + sco_chan_del(sk, ECONNRESET); + break; + case BT_CONNECT: case BT_DISCONN: sco_chan_del(sk, ECONNRESET); @@ -819,7 +828,9 @@ static void sco_chan_del(struct sock *sk, int err) conn->sk = NULL; sco_pi(sk)->conn = NULL; sco_conn_unlock(conn); - hci_conn_put(conn->hcon); + + if (conn->hcon) + hci_conn_put(conn->hcon); } sk->sk_state = BT_CLOSED; -- cgit v1.1 From 6de6c18d8d3b2a82cc1c657f005e61b1c1f2f886 Mon Sep 17 00:00:00 2001 From: Ville Tervo Date: Fri, 27 May 2011 11:16:21 +0300 Subject: Bluetooth: Do not send SET_EVENT_MASK for 1.1 and earlier devices Some old hci controllers do not accept any mask so leave the default mask on for these devices. < HCI Command: Set Event Mask (0x03|0x0001) plen 8 Mask: 0xfffffbff00000000 > HCI Event: Command Complete (0x0e) plen 4 Set Event Mask (0x03|0x0001) ncmd 1 status 0x12 Error: Invalid HCI Command Parameters Signed-off-by: Ville Tervo Tested-by: Corey Boyle Tested-by: Ed Tomlinson Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f13ddbf..77930aa 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -477,14 +477,16 @@ static void hci_setup_event_mask(struct hci_dev *hdev) * command otherwise */ u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 }; - /* Events for 1.2 and newer controllers */ - if (hdev->lmp_ver > 1) { - events[4] |= 0x01; /* Flow Specification Complete */ - events[4] |= 0x02; /* Inquiry Result with RSSI */ - events[4] |= 0x04; /* Read Remote Extended Features Complete */ - events[5] |= 0x08; /* Synchronous Connection Complete */ - events[5] |= 0x10; /* Synchronous Connection Changed */ - } + /* CSR 1.1 dongles does not accept any bitfield so don't try to set + * any event mask for pre 1.2 devices */ + if (hdev->lmp_ver <= 1) + return; + + events[4] |= 0x01; /* Flow Specification Complete */ + events[4] |= 0x02; /* Inquiry Result with RSSI */ + events[4] |= 0x04; /* Read Remote Extended Features Complete */ + events[5] |= 0x08; /* Synchronous Connection Complete */ + events[5] |= 0x10; /* Synchronous Connection Changed */ if (hdev->features[3] & LMP_RSSI_INQ) events[4] |= 0x04; /* Inquiry Result with RSSI */ -- cgit v1.1 From d29d04ce0f49b9fbcaefc83ec4e9bdb7badd45fa Mon Sep 17 00:00:00 2001 From: David Miller Date: Thu, 19 May 2011 17:50:05 -0400 Subject: Bluetooth: Kill set but unused variable 'cmd' in cmtp_recv_capimsg() Signed-off-by: David S. Miller Signed-off-by: Gustavo F. Padovan --- net/bluetooth/cmtp/capi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c index 744233c..040f67b 100644 --- a/net/bluetooth/cmtp/capi.c +++ b/net/bluetooth/cmtp/capi.c @@ -326,7 +326,7 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) { struct capi_ctr *ctrl = &session->ctrl; struct cmtp_application *application; - __u16 cmd, appl; + __u16 appl; __u32 contr; BT_DBG("session %p skb %p len %d", session, skb, skb->len); @@ -344,7 +344,6 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) return; } - cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data)); appl = CAPIMSG_APPID(skb->data); contr = CAPIMSG_CONTROL(skb->data); -- cgit v1.1 From 1d34d108e07680e2c07847d5e69a334cb4f96ab3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 6 Jun 2011 12:59:29 +0300 Subject: mac80211: add ieee80211_get_operstate() function Add ieee80211_get_operstate() function to get the operstate of the netdevice. This is needed for drivers that need to know when the interface is IF_OPER_UP (e.g. wl12xx), and block notifiers can't be used (e.g. because the interface is already IF_OPER_UP, like after resuming from suspend) Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d595265..0c6e9ef 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2652,3 +2652,10 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); } EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); + +unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + return sdata->dev->operstate; +} +EXPORT_SYMBOL(ieee80211_get_operstate); -- cgit v1.1 From 323222b5ff930a43eab45cec6e58345740fa2a29 Mon Sep 17 00:00:00 2001 From: Paul Stewart Date: Wed, 8 Jun 2011 05:52:52 -0700 Subject: cfg80211: Ignore downstream DEAUTH for authtry_bsses Downsteram DEAUTH messages do not refer to a current authentication attempt -- AUTH responses do. Therefore we should not allow DEAUTH from an AP to void state for an AUTH attempt in progress. Signed-off-by: Paul Stewart Signed-off-by: John W. Linville --- net/wireless/mlme.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 493b939..3633ab6 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -170,7 +170,9 @@ void __cfg80211_send_deauth(struct net_device *dev, break; } if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { + memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, + ETH_ALEN) == 0 && + memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) == 0) { cfg80211_unhold_bss(wdev->authtry_bsses[i]); cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); wdev->authtry_bsses[i] = NULL; -- cgit v1.1 From 43a1c2721acd792aea370ee68ef054e18c944373 Mon Sep 17 00:00:00 2001 From: Vincent Zweije Date: Tue, 7 Jun 2011 16:37:09 +0200 Subject: networking: fix warning about unused label wake_up Function ieee80211_reconfig in net/mac80211/util.c contains label wake_up which is defined unconditionally, but only used with CONFIG_PM. Gcc warns about this when CONFIG_PM is not defined. This patch makes the label's definition dependent on CONFIG_PM too, eliminating the warning. The issue was apparently introduced in git commit eecc48000afe2ca6da22122d553b7cad294e42fc. Signed-off-by: Vincent Zweije Signed-off-by: John W. Linville --- net/mac80211/util.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d3fe2d2..05e3fb8 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1276,7 +1276,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (ieee80211_sdata_running(sdata)) ieee80211_enable_keys(sdata); +#ifdef CONFIG_PM wake_up: +#endif ieee80211_wake_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); -- cgit v1.1 From 6fdf658c9a0e51e6663f2769f6d310c2843a862b Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 13 Jun 2011 15:37:35 +0300 Subject: Bluetooth: Fix L2CAP security check With older userspace versions (using hciops) it might not have the key type to check if the key has sufficient security for any security level so it is necessary to check the return of hci_conn_auth to make sure the connection is authenticated Signed-off-by: Luiz Augusto von Dentz Acked-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 3163330..b9aa986 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -611,8 +611,8 @@ auth: if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) return 0; - hci_conn_auth(conn, sec_level, auth_type); - return 0; + if (!hci_conn_auth(conn, sec_level, auth_type)) + return 0; encrypt: if (conn->link_mode & HCI_LM_ENCRYPT) -- cgit v1.1 From dc50a06dac61d7ca7ddb3d9bb8921ca5d68f51b6 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 16 May 2011 16:42:01 -0300 Subject: Bluetooth: Merge l2cap_chan_create() in the l2cap_sock_alloc() As a first step to remove l2cap_sock_alloc() and l2cap_sock_init() from l2cap_core.c Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 18 +++--------------- net/bluetooth/l2cap_sock.c | 18 +++++++++--------- 2 files changed, 12 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index dff9d76..49f890b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -846,13 +846,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) if (!sk) goto clean; - chan = l2cap_chan_create(sk); - if (!chan) { - l2cap_sock_kill(sk); - goto clean; - } - - l2cap_pi(sk)->chan = chan; + chan = l2cap_pi(sk)->chan; write_lock_bh(&conn->chan_lock); @@ -2339,14 +2333,6 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd if (!sk) goto response; - chan = l2cap_chan_create(sk); - if (!chan) { - l2cap_sock_kill(sk); - goto response; - } - - l2cap_pi(sk)->chan = chan; - write_lock_bh(&conn->chan_lock); /* Check if we already have channel with that dcid */ @@ -2359,6 +2345,8 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd hci_conn_hold(conn->hcon); + chan = l2cap_pi(sk)->chan; + l2cap_sock_init(sk, parent); bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index ab81894..2f4fd57 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -853,6 +853,7 @@ static struct proto l2cap_proto = { struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) { struct sock *sk; + struct l2cap_chan *chan; sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); if (!sk) @@ -869,6 +870,14 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g sk->sk_protocol = proto; sk->sk_state = BT_OPEN; + chan = l2cap_chan_create(sk); + if (!chan) { + l2cap_sock_kill(sk); + return NULL; + } + + l2cap_pi(sk)->chan = chan; + return sk; } @@ -876,7 +885,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, int kern) { struct sock *sk; - struct l2cap_chan *chan; BT_DBG("sock %p", sock); @@ -895,14 +903,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, if (!sk) return -ENOMEM; - chan = l2cap_chan_create(sk); - if (!chan) { - l2cap_sock_kill(sk); - return -ENOMEM; - } - - l2cap_pi(sk)->chan = chan; - l2cap_sock_init(sk, NULL); return 0; } -- cgit v1.1 From 80808e431e1ef25856457de82ce141bed6a6313a Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 16 May 2011 17:24:37 -0300 Subject: Bluetooth: Add l2cap_chan_ops abstraction Add an abstraction layer between L2CAP core and its users (only l2cap_sock.c now). The first function implemented is new_connection() that replaces calls to l2cap_sock_alloc() in l2cap_core.c Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 17 +++++++---------- net/bluetooth/l2cap_sock.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 49f890b..8369f56 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -842,18 +842,16 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) goto clean; } - sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC); - if (!sk) + chan = pchan->ops->new_connection(pchan->data); + if (!chan) goto clean; - chan = l2cap_pi(sk)->chan; + sk = chan->sk; write_lock_bh(&conn->chan_lock); hci_conn_hold(conn->hcon); - l2cap_sock_init(sk, parent); - bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); @@ -2329,10 +2327,12 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd goto response; } - sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC); - if (!sk) + chan = pchan->ops->new_connection(pchan->data); + if (!chan) goto response; + sk = chan->sk; + write_lock_bh(&conn->chan_lock); /* Check if we already have channel with that dcid */ @@ -2345,9 +2345,6 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd hci_conn_hold(conn->hcon); - chan = l2cap_pi(sk)->chan; - - l2cap_sock_init(sk, parent); bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); chan->psm = psm; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 2f4fd57..4050ede 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -31,6 +31,8 @@ #include static const struct proto_ops l2cap_sock_ops; +static void l2cap_sock_init(struct sock *sk, struct sock *parent); +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { @@ -773,6 +775,25 @@ static int l2cap_sock_release(struct socket *sock) return err; } +static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) +{ + struct sock *sk, *parent = data; + + sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, + GFP_ATOMIC); + if (!sk) + return NULL; + + l2cap_sock_init(sk, parent); + + return l2cap_pi(sk)->chan; +} + +static struct l2cap_ops l2cap_chan_ops = { + .name = "L2CAP Socket Interface", + .new_connection = l2cap_sock_new_connection_cb, +}; + static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); @@ -781,7 +802,7 @@ static void l2cap_sock_destruct(struct sock *sk) skb_queue_purge(&sk->sk_write_queue); } -void l2cap_sock_init(struct sock *sk, struct sock *parent) +static void l2cap_sock_init(struct sock *sk, struct sock *parent) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_chan *chan = pi->chan; @@ -838,10 +859,14 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->force_reliable = 0; chan->flushable = BT_FLUSHABLE_OFF; chan->force_active = BT_POWER_FORCE_ACTIVE_ON; + } /* Default config options */ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; + + chan->data = sk; + chan->ops = &l2cap_chan_ops; } static struct proto l2cap_proto = { @@ -850,7 +875,7 @@ static struct proto l2cap_proto = { .obj_size = sizeof(struct l2cap_pinfo) }; -struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) { struct sock *sk; struct l2cap_chan *chan; -- cgit v1.1 From 230704942283cb3990584ddd6955ac8decfa6a2c Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 16 May 2011 17:57:22 -0300 Subject: Bluetooth: add recv() callback to l2cap_chan_ops This abstracts the call to sock_queue_recv_skb() into l2cap_chan_ops->recv(). Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 16 ++++++++-------- net/bluetooth/l2cap_sock.c | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8369f56..4cbb48d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1698,7 +1698,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) if (!nskb) continue; - if (sock_queue_rcv_skb(sk, nskb)) + if (chan->ops->recv(chan->data, nskb)) kfree_skb(nskb); } read_unlock(&conn->chan_lock); @@ -3124,7 +3124,7 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk if (chan->conn_state & L2CAP_CONN_SAR_SDU) goto drop; - return sock_queue_rcv_skb(chan->sk, skb); + return chan->ops->recv(chan->data, skb); case L2CAP_SDU_START: if (chan->conn_state & L2CAP_CONN_SAR_SDU) @@ -3190,7 +3190,7 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk return -ENOMEM; } - err = sock_queue_rcv_skb(chan->sk, _skb); + err = chan->ops->recv(chan->data, _skb); if (err < 0) { kfree_skb(_skb); chan->conn_state |= L2CAP_CONN_SAR_RETRY; @@ -3358,7 +3358,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf break; } - err = sock_queue_rcv_skb(chan->sk, skb); + err = chan->ops->recv(chan->data, skb); if (!err) return 0; @@ -3419,7 +3419,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf if (chan->partial_sdu_len == chan->sdu_len) { _skb = skb_clone(chan->sdu, GFP_ATOMIC); - err = sock_queue_rcv_skb(chan->sk, _skb); + err = chan->ops->recv(chan->data, _skb); if (err < 0) kfree_skb(_skb); } @@ -3886,7 +3886,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (chan->imtu < skb->len) goto drop; - if (!sock_queue_rcv_skb(sk, skb)) + if (!chan->ops->recv(chan->data, skb)) goto done; break; @@ -3964,7 +3964,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str if (l2cap_pi(sk)->chan->imtu < skb->len) goto drop; - if (!sock_queue_rcv_skb(sk, skb)) + if (!chan->ops->recv(chan->data, skb)) goto done; drop: @@ -3997,7 +3997,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct if (l2cap_pi(sk)->chan->imtu < skb->len) goto drop; - if (!sock_queue_rcv_skb(sk, skb)) + if (!chan->ops->recv(chan->data, skb)) goto done; drop: diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 4050ede..28cdc7e 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -789,9 +789,17 @@ 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) +{ + struct sock *sk = data; + + return sock_queue_rcv_skb(sk, skb); +} + static struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, + .recv = l2cap_sock_recv_cb, }; static void l2cap_sock_destruct(struct sock *sk) -- cgit v1.1 From ba3bd0ee3946d9300512e685e8d5573dfa10c060 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Mon, 16 May 2011 18:23:24 -0300 Subject: Bluetooth: add close() callback to l2cap_chan_ops close() calls l2cap_sock_kill() on l2cap_sock.c Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 17 +++++++++-------- net/bluetooth/l2cap_sock.c | 10 +++++++++- 2 files changed, 18 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4cbb48d..3a121ac6b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -254,7 +254,7 @@ static void l2cap_chan_timeout(unsigned long arg) bh_unlock_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); sock_put(sk); } @@ -391,11 +391,12 @@ static void l2cap_chan_cleanup_listen(struct sock *parent) /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) { - l2cap_chan_clear_timer(l2cap_pi(sk)->chan); + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + l2cap_chan_clear_timer(chan); lock_sock(sk); - l2cap_chan_close(l2cap_pi(sk)->chan, ECONNRESET); + l2cap_chan_close(chan, ECONNRESET); release_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); } parent->sk_state = BT_CLOSED; @@ -993,7 +994,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) bh_lock_sock(sk); l2cap_chan_del(chan, err); bh_unlock_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); } if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) @@ -2339,7 +2340,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd if (__l2cap_get_chan_by_dcid(conn, scid)) { write_unlock_bh(&conn->chan_lock); sock_set_flag(sk, SOCK_ZAPPED); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); goto response; } @@ -2712,7 +2713,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd l2cap_chan_del(chan, ECONNRESET); bh_unlock_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); return 0; } @@ -2746,7 +2747,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd l2cap_chan_del(chan, 0); bh_unlock_sock(sk); - l2cap_sock_kill(sk); + chan->ops->close(chan->data); return 0; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 28cdc7e..9f15a16 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -713,7 +713,7 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket. */ -void l2cap_sock_kill(struct sock *sk) +static void l2cap_sock_kill(struct sock *sk) { if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) return; @@ -796,10 +796,18 @@ static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) return sock_queue_rcv_skb(sk, skb); } +static void l2cap_sock_close_cb(void *data) +{ + struct sock *sk = data; + + l2cap_sock_kill(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, }; static void l2cap_sock_destruct(struct sock *sk) -- cgit v1.1 From 89bc500e41fc5b48e0573e6b0d927fc97b8951dc Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 3 Jun 2011 00:19:47 -0300 Subject: Bluetooth: Add state tracking to struct l2cap_chan Now socket state is tracked by struct sock and channel state is tracked by chan->state. At this point both says the same, but this is going to change when we add AMP Support for example. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 113 +++++++++++++++++++++++---------------------- net/bluetooth/l2cap_sock.c | 12 +++++ 2 files changed, 71 insertions(+), 54 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3a121ac6b..58fe03a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -210,27 +210,33 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) { - BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->sk->sk_state, - timeout); + BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); + if (!mod_timer(&chan->chan_timer, jiffies + timeout)) sock_hold(chan->sk); } static void l2cap_chan_clear_timer(struct l2cap_chan *chan) { - BT_DBG("chan %p state %d", chan, chan->sk->sk_state); + BT_DBG("chan %p state %d", chan, chan->state); if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer)) __sock_put(chan->sk); } +static void l2cap_state_change(struct l2cap_chan *chan, int state) +{ + chan->state = state; + chan->ops->state_change(chan->data, state); +} + static void l2cap_chan_timeout(unsigned long arg) { struct l2cap_chan *chan = (struct l2cap_chan *) arg; struct sock *sk = chan->sk; int reason; - BT_DBG("chan %p state %d", chan, sk->sk_state); + BT_DBG("chan %p state %d", chan, chan->state); bh_lock_sock(sk); @@ -242,9 +248,9 @@ static void l2cap_chan_timeout(unsigned long arg) return; } - if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) + if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) reason = ECONNREFUSED; - else if (sk->sk_state == BT_CONNECT && + else if (chan->state == BT_CONNECT && chan->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else @@ -274,6 +280,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) setup_timer(&chan->chan_timer, l2cap_chan_timeout, (unsigned long) chan); + chan->state = BT_OPEN; + return chan; } @@ -348,7 +356,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) hci_conn_put(conn->hcon); } - sk->sk_state = BT_CLOSED; + l2cap_state_change(chan, BT_CLOSED); sock_set_flag(sk, SOCK_ZAPPED); if (err) @@ -398,9 +406,6 @@ static void l2cap_chan_cleanup_listen(struct sock *parent) release_sock(sk); chan->ops->close(chan->data); } - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); } void l2cap_chan_close(struct l2cap_chan *chan, int reason) @@ -408,11 +413,14 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) struct l2cap_conn *conn = chan->conn; struct sock *sk = chan->sk; - BT_DBG("chan %p state %d socket %p", chan, sk->sk_state, sk->sk_socket); + BT_DBG("chan %p state %d socket %p", chan, chan->state, sk->sk_socket); - switch (sk->sk_state) { + switch (chan->state) { case BT_LISTEN: l2cap_chan_cleanup_listen(sk); + + l2cap_state_change(chan, BT_CLOSED); + sock_set_flag(sk, SOCK_ZAPPED); break; case BT_CONNECTED: @@ -436,7 +444,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) result = L2CAP_CR_SEC_BLOCK; else result = L2CAP_CR_BAD_PSM; - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); @@ -548,13 +556,11 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) { struct sk_buff *skb; struct l2cap_hdr *lh; - struct l2cap_pinfo *pi = l2cap_pi(chan->sk); struct l2cap_conn *conn = chan->conn; - struct sock *sk = (struct sock *)pi; int count, hlen = L2CAP_HDR_SIZE + 2; u8 flags; - if (sk->sk_state != BT_CONNECTED) + if (chan->state != BT_CONNECTED) return; if (chan->fcs == L2CAP_FCS_CRC16) @@ -689,7 +695,7 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, sizeof(req), &req); - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); sk->sk_err = err; } @@ -712,7 +718,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) continue; } - if (sk->sk_state == BT_CONNECT) { + if (chan->state == BT_CONNECT) { struct l2cap_conn_req req; if (!l2cap_check_security(chan) || @@ -728,7 +734,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) /* l2cap_chan_close() calls list_del(chan) * so release the lock */ read_unlock_bh(&conn->chan_lock); - l2cap_chan_close(chan, ECONNRESET); + l2cap_chan_close(chan, ECONNRESET); read_lock_bh(&conn->chan_lock); bh_unlock_sock(sk); continue; @@ -743,7 +749,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); - } else if (sk->sk_state == BT_CONNECT2) { + } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; char buf[128]; rsp.scid = cpu_to_le16(chan->dcid); @@ -757,7 +763,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) parent->sk_data_ready(parent, 0); } else { - sk->sk_state = BT_CONFIG; + l2cap_state_change(chan, BT_CONFIG); rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); } @@ -799,7 +805,7 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, __le16 cid, bdadd list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; - if (state && sk->sk_state != state) + if (state && c->state != state) continue; if (c->scid == cid) { @@ -862,7 +868,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) l2cap_chan_set_timer(chan, sk->sk_sndtimeo); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); parent->sk_data_ready(parent, 0); write_unlock_bh(&conn->chan_lock); @@ -889,15 +895,15 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) if (conn->hcon->type == LE_LINK) { l2cap_chan_clear_timer(chan); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); } if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { l2cap_chan_clear_timer(chan); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); - } else if (sk->sk_state == BT_CONNECT) + } else if (chan->state == BT_CONNECT) l2cap_do_start(chan); bh_unlock_sock(sk); @@ -1025,7 +1031,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; - if (state && sk->sk_state != state) + if (state && c->state != state) continue; if (c->psm == psm) { @@ -1092,14 +1098,14 @@ int l2cap_chan_connect(struct l2cap_chan *chan) l2cap_chan_add(conn, chan); - sk->sk_state = BT_CONNECT; + l2cap_state_change(chan, BT_CONNECT); l2cap_chan_set_timer(chan, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { l2cap_chan_clear_timer(chan); if (l2cap_check_security(chan)) - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); } else l2cap_do_start(chan); } @@ -1288,11 +1294,10 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) int l2cap_ertm_send(struct l2cap_chan *chan) { struct sk_buff *skb, *tx_skb; - struct sock *sk = chan->sk; u16 control, fcs; int nsent = 0; - if (sk->sk_state != BT_CONNECTED) + if (chan->state != BT_CONNECTED) return -ENOTCONN; while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) { @@ -1668,7 +1673,7 @@ static void l2cap_chan_ready(struct sock *sk) /* Outgoing channel. * Wake up socket sleeping on connect. */ - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); } else { /* Incoming channel. @@ -2364,22 +2369,22 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { if (l2cap_check_security(chan)) { if (bt_sk(sk)->defer_setup) { - sk->sk_state = BT_CONNECT2; + l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHOR_PEND; parent->sk_data_ready(parent, 0); } else { - sk->sk_state = BT_CONFIG; + l2cap_state_change(chan, BT_CONFIG); result = L2CAP_CR_SUCCESS; status = L2CAP_CS_NO_INFO; } } else { - sk->sk_state = BT_CONNECT2; + l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHEN_PEND; } } else { - sk->sk_state = BT_CONNECT2; + l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_NO_INFO; } @@ -2451,7 +2456,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd switch (result) { case L2CAP_CR_SUCCESS: - sk->sk_state = BT_CONFIG; + l2cap_state_change(chan, BT_CONFIG); chan->ident = 0; chan->dcid = dcid; chan->conf_state &= ~L2CAP_CONF_CONNECT_PEND; @@ -2473,7 +2478,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd default: /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ / 5); break; @@ -2520,7 +2525,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr sk = chan->sk; - if (sk->sk_state != BT_CONFIG) { + if (chan->state != BT_CONFIG) { struct l2cap_cmd_rej rej; rej.reason = cpu_to_le16(0x0002); @@ -2569,7 +2574,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (chan->conf_state & L2CAP_CONF_INPUT_DONE) { set_default_fcs(chan); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); chan->next_tx_seq = 0; chan->expected_tx_seq = 0; @@ -2661,7 +2666,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (chan->conf_state & L2CAP_CONF_OUTPUT_DONE) { set_default_fcs(chan); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); chan->next_tx_seq = 0; chan->expected_tx_seq = 0; skb_queue_head_init(&chan->tx_q); @@ -2703,7 +2708,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); @@ -2737,7 +2742,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan,BT_DISCONN); l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); @@ -3874,7 +3879,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk BT_DBG("chan %p, len %d", chan, skb->len); - if (sk->sk_state != BT_CONNECTED) + if (chan->state != BT_CONNECTED) goto drop; switch (chan->mode) { @@ -3959,7 +3964,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str BT_DBG("sk %p, len %d", sk, skb->len); - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED) + if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) goto drop; if (l2cap_pi(sk)->chan->imtu < skb->len) @@ -3992,7 +3997,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct BT_DBG("sk %p, len %d", sk, skb->len); - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_CONNECTED) + if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) goto drop; if (l2cap_pi(sk)->chan->imtu < skb->len) @@ -4066,7 +4071,7 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; - if (sk->sk_state != BT_LISTEN) + if (c->state != BT_LISTEN) continue; if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { @@ -4167,14 +4172,14 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) continue; } - if (!status && (sk->sk_state == BT_CONNECTED || - sk->sk_state == BT_CONFIG)) { + if (!status && (chan->state == BT_CONNECTED || + chan->state == BT_CONFIG)) { l2cap_check_encryption(chan, encrypt); bh_unlock_sock(sk); continue; } - if (sk->sk_state == BT_CONNECT) { + if (chan->state == BT_CONNECT) { if (!status) { struct l2cap_conn_req req; req.scid = cpu_to_le16(chan->scid); @@ -4189,15 +4194,15 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) l2cap_chan_clear_timer(chan); l2cap_chan_set_timer(chan, HZ / 10); } - } else if (sk->sk_state == BT_CONNECT2) { + } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; __u16 result; if (!status) { - sk->sk_state = BT_CONFIG; + l2cap_state_change(chan, BT_CONFIG); result = L2CAP_CR_SUCCESS; } else { - sk->sk_state = BT_DISCONN; + l2cap_state_change(chan, BT_DISCONN); l2cap_chan_set_timer(chan, HZ / 10); result = L2CAP_CR_SEC_BLOCK; } @@ -4341,7 +4346,7 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p) seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst), - sk->sk_state, __le16_to_cpu(c->psm), + c->state, __le16_to_cpu(c->psm), c->scid, c->dcid, c->imtu, c->omtu, c->sec_level, c->mode); } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 9f15a16..1d9c365 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -89,6 +89,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) chan->sec_level = BT_SECURITY_SDP; bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + + chan->state = BT_BOUND; sk->sk_state = BT_BOUND; done: @@ -214,6 +216,8 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; + + chan->state = BT_LISTEN; sk->sk_state = BT_LISTEN; done: @@ -803,11 +807,19 @@ static void l2cap_sock_close_cb(void *data) l2cap_sock_kill(sk); } +static void l2cap_sock_state_change_cb(void *data, int state) +{ + struct sock *sk = data; + + sk->sk_state = state; +} + 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, + .state_change = l2cap_sock_state_change_cb, }; static void l2cap_sock_destruct(struct sock *sk) -- cgit v1.1 From 71ba0e569bb43ab99a07ccbb514f8b0f732140c3 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 17 May 2011 14:34:52 -0300 Subject: Bluetooth: Add refcnt to struct l2cap_chan struct l2cap_chan has now its own refcnt that is compatible with the socket refcnt, i.e., we won't see sk_refcnt = 0 and chan->refcnt > 0. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 58fe03a..f24022c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -78,6 +78,18 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb); /* ---- L2CAP channels ---- */ + +static inline void chan_hold(struct l2cap_chan *c) +{ + atomic_inc(&c->refcnt); +} + +static inline void chan_put(struct l2cap_chan *c) +{ + if (atomic_dec_and_test(&c->refcnt)) + kfree(c); +} + static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) { struct l2cap_chan *c; @@ -213,7 +225,7 @@ static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); if (!mod_timer(&chan->chan_timer, jiffies + timeout)) - sock_hold(chan->sk); + chan_hold(chan); } static void l2cap_chan_clear_timer(struct l2cap_chan *chan) @@ -221,7 +233,7 @@ static void l2cap_chan_clear_timer(struct l2cap_chan *chan) BT_DBG("chan %p state %d", chan, chan->state); if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer)) - __sock_put(chan->sk); + chan_put(chan); } static void l2cap_state_change(struct l2cap_chan *chan, int state) @@ -244,7 +256,7 @@ static void l2cap_chan_timeout(unsigned long arg) /* sk is owned by user. Try again later */ l2cap_chan_set_timer(chan, HZ / 5); bh_unlock_sock(sk); - sock_put(sk); + chan_put(chan); return; } @@ -261,7 +273,7 @@ static void l2cap_chan_timeout(unsigned long arg) bh_unlock_sock(sk); chan->ops->close(chan->data); - sock_put(sk); + chan_put(chan); } struct l2cap_chan *l2cap_chan_create(struct sock *sk) @@ -282,6 +294,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) chan->state = BT_OPEN; + atomic_set(&chan->refcnt, 1); + return chan; } @@ -291,13 +305,11 @@ void l2cap_chan_destroy(struct l2cap_chan *chan) list_del(&chan->global_l); write_unlock_bh(&chan_list_lock); - kfree(chan); + chan_put(chan); } static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { - struct sock *sk = chan->sk; - BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, chan->psm, chan->dcid); @@ -328,7 +340,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->omtu = L2CAP_DEFAULT_MTU; } - sock_hold(sk); + chan_hold(chan); list_add(&chan->list, &conn->chan_l); } @@ -350,7 +362,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) write_lock_bh(&conn->chan_lock); list_del(&chan->list); write_unlock_bh(&conn->chan_lock); - __sock_put(sk); + chan_put(chan); chan->conn = NULL; hci_conn_put(conn->hcon); -- cgit v1.1 From c9b66675373e6edb2dc291562ce1fa05f7980102 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 17 May 2011 14:59:01 -0300 Subject: Bluetooth: Make timer functions generic We now plan to use l2cap_set_timer and l2cap_clear_timer in ERTM timers. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 58 +++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f24022c..5076976 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -220,19 +220,19 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) return 0; } -static void l2cap_chan_set_timer(struct l2cap_chan *chan, long timeout) +static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout) { BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); - if (!mod_timer(&chan->chan_timer, jiffies + timeout)) + if (!mod_timer(timer, jiffies + timeout)) chan_hold(chan); } -static void l2cap_chan_clear_timer(struct l2cap_chan *chan) +static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer) { BT_DBG("chan %p state %d", chan, chan->state); - if (timer_pending(&chan->chan_timer) && del_timer(&chan->chan_timer)) + if (timer_pending(timer) && del_timer(timer)) chan_put(chan); } @@ -254,7 +254,7 @@ static void l2cap_chan_timeout(unsigned long arg) if (sock_owned_by_user(sk)) { /* sk is owned by user. Try again later */ - l2cap_chan_set_timer(chan, HZ / 5); + __set_chan_timer(chan, HZ / 5); bh_unlock_sock(sk); chan_put(chan); return; @@ -353,7 +353,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) struct l2cap_conn *conn = chan->conn; struct sock *parent = bt_sk(sk)->parent; - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); BT_DBG("chan %p, conn %p, err %d", chan, conn, err); @@ -412,7 +412,7 @@ static void l2cap_chan_cleanup_listen(struct sock *parent) /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) { struct l2cap_chan *chan = l2cap_pi(sk)->chan; - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); lock_sock(sk); l2cap_chan_close(chan, ECONNRESET); release_sock(sk); @@ -439,8 +439,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, sk->sk_sndtimeo); + __clear_chan_timer(chan); + __set_chan_timer(chan, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); } else l2cap_chan_del(chan, reason); @@ -878,7 +878,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) __l2cap_chan_add(conn, chan); - l2cap_chan_set_timer(chan, sk->sk_sndtimeo); + __set_chan_timer(chan, sk->sk_sndtimeo); l2cap_state_change(chan, BT_CONNECTED); parent->sk_data_ready(parent, 0); @@ -906,13 +906,13 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) bh_lock_sock(sk); if (conn->hcon->type == LE_LINK) { - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); } if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); } else if (chan->state == BT_CONNECT) @@ -1111,11 +1111,11 @@ int l2cap_chan_connect(struct l2cap_chan *chan) l2cap_chan_add(conn, chan); l2cap_state_change(chan, BT_CONNECT); - l2cap_chan_set_timer(chan, sk->sk_sndtimeo); + __set_chan_timer(chan, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); if (l2cap_check_security(chan)) l2cap_state_change(chan, BT_CONNECTED); } else @@ -1679,7 +1679,7 @@ static void l2cap_chan_ready(struct sock *sk) BT_DBG("sk %p, parent %p", sk, parent); chan->conf_state = 0; - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); if (!parent) { /* Outgoing channel. @@ -2374,7 +2374,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd dcid = chan->scid; - l2cap_chan_set_timer(chan, sk->sk_sndtimeo); + __set_chan_timer(chan, sk->sk_sndtimeo); chan->ident = cmd->ident; @@ -2491,8 +2491,8 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { l2cap_state_change(chan, BT_DISCONN); - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ / 5); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ / 5); break; } @@ -2665,7 +2665,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr default: sk->sk_err = ECONNRESET; - l2cap_chan_set_timer(chan, HZ * 5); + __set_chan_timer(chan, HZ * 5); l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } @@ -2721,8 +2721,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { l2cap_state_change(chan, BT_DISCONN); - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ / 5); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ / 5); bh_unlock_sock(sk); return 0; } @@ -2755,8 +2755,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { l2cap_state_change(chan,BT_DISCONN); - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ / 5); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ / 5); bh_unlock_sock(sk); return 0; } @@ -4152,13 +4152,13 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) if (encrypt == 0x00) { if (chan->sec_level == BT_SECURITY_MEDIUM) { - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ * 5); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ * 5); } else if (chan->sec_level == BT_SECURITY_HIGH) l2cap_chan_close(chan, ECONNREFUSED); } else { if (chan->sec_level == BT_SECURITY_MEDIUM) - l2cap_chan_clear_timer(chan); + __clear_chan_timer(chan); } } @@ -4203,8 +4203,8 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } else { - l2cap_chan_clear_timer(chan); - l2cap_chan_set_timer(chan, HZ / 10); + __clear_chan_timer(chan); + __set_chan_timer(chan, HZ / 10); } } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; @@ -4215,7 +4215,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) result = L2CAP_CR_SUCCESS; } else { l2cap_state_change(chan, BT_DISCONN); - l2cap_chan_set_timer(chan, HZ / 10); + __set_chan_timer(chan, HZ / 10); result = L2CAP_CR_SEC_BLOCK; } -- cgit v1.1 From 1a09bcb97ca1b4eb9a6ea381fbc3beb7a9d2895d Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 17 May 2011 15:13:19 -0300 Subject: Bluetooth: keep reference if any ERTM timer is enabled ERTM use the generic L2CAP timer functions to keep a reference to the channel. This is useful for avoiding crashes. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 5076976..3b31a1f 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -389,9 +389,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) if (chan->mode == L2CAP_MODE_ERTM) { struct srej_list *l, *tmp; - del_timer(&chan->retrans_timer); - del_timer(&chan->monitor_timer); - del_timer(&chan->ack_timer); + __clear_retrans_timer(chan); + __clear_monitor_timer(chan); + __clear_ack_timer(chan); skb_queue_purge(&chan->srej_q); skb_queue_purge(&chan->busy_q); @@ -697,9 +697,9 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c sk = chan->sk; if (chan->mode == L2CAP_MODE_ERTM) { - del_timer(&chan->retrans_timer); - del_timer(&chan->monitor_timer); - del_timer(&chan->ack_timer); + __clear_retrans_timer(chan); + __clear_monitor_timer(chan); + __clear_ack_timer(chan); } req.dcid = cpu_to_le16(chan->dcid); @@ -1177,7 +1177,7 @@ static void l2cap_monitor_timeout(unsigned long arg) } chan->retry_count++; - __mod_monitor_timer(); + __set_monitor_timer(chan); l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); bh_unlock_sock(sk); @@ -1192,7 +1192,7 @@ static void l2cap_retrans_timeout(unsigned long arg) bh_lock_sock(sk); chan->retry_count = 1; - __mod_monitor_timer(); + __set_monitor_timer(chan); chan->conn_state |= L2CAP_CONN_WAIT_F; @@ -1216,7 +1216,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan) } if (!chan->unacked_frames) - del_timer(&chan->retrans_timer); + __clear_retrans_timer(chan); } void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) @@ -1343,7 +1343,7 @@ int l2cap_ertm_send(struct l2cap_chan *chan) l2cap_do_send(chan, tx_skb); - __mod_retrans_timer(); + __set_retrans_timer(chan); bt_cb(skb)->tx_seq = chan->next_tx_seq; chan->next_tx_seq = (chan->next_tx_seq + 1) % 64; @@ -3260,8 +3260,8 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) l2cap_send_sframe(chan, control); chan->retry_count = 1; - del_timer(&chan->retrans_timer); - __mod_monitor_timer(); + __clear_retrans_timer(chan); + __set_monitor_timer(chan); chan->conn_state |= L2CAP_CONN_WAIT_F; @@ -3352,7 +3352,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c chan->conn_state |= L2CAP_CONN_RNR_SENT; - del_timer(&chan->ack_timer); + __clear_ack_timer(chan); queue_work(_busy_wq, &chan->busy_work); @@ -3521,9 +3521,9 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont if (L2CAP_CTRL_FINAL & rx_control && chan->conn_state & L2CAP_CONN_WAIT_F) { - del_timer(&chan->monitor_timer); + __clear_monitor_timer(chan); if (chan->unacked_frames > 0) - __mod_retrans_timer(); + __set_retrans_timer(chan); chan->conn_state &= ~L2CAP_CONN_WAIT_F; } @@ -3604,7 +3604,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont l2cap_send_srejframe(chan, tx_seq); - del_timer(&chan->ack_timer); + __clear_ack_timer(chan); } return 0; @@ -3629,7 +3629,7 @@ expected: l2cap_retransmit_frames(chan); } - __mod_ack_timer(); + __set_ack_timer(chan); chan->num_acked = (chan->num_acked + 1) % num_to_ack; if (chan->num_acked == num_to_ack - 1) @@ -3655,7 +3655,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && (chan->unacked_frames > 0)) - __mod_retrans_timer(); + __set_retrans_timer(chan); chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; l2cap_send_srejtail(chan); @@ -3674,7 +3674,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co } else { if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && (chan->unacked_frames > 0)) - __mod_retrans_timer(); + __set_retrans_timer(chan); chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; if (chan->conn_state & L2CAP_CONN_SREJ_SENT) @@ -3757,7 +3757,7 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c chan->conn_state |= L2CAP_CONN_SEND_FBIT; if (!(chan->conn_state & L2CAP_CONN_SREJ_SENT)) { - del_timer(&chan->retrans_timer); + __clear_retrans_timer(chan); if (rx_control & L2CAP_CTRL_POLL) l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); return; @@ -3775,9 +3775,9 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont if (L2CAP_CTRL_FINAL & rx_control && chan->conn_state & L2CAP_CONN_WAIT_F) { - del_timer(&chan->monitor_timer); + __clear_monitor_timer(chan); if (chan->unacked_frames > 0) - __mod_retrans_timer(); + __set_retrans_timer(chan); chan->conn_state &= ~L2CAP_CONN_WAIT_F; } -- cgit v1.1 From b569450682e944653f307b47c549ca12150d4596 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 8 Jun 2011 19:09:13 -0300 Subject: Bluetooth: Don't forget to check for LE_LINK Otherwise the wrong error can be returned. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3b31a1f..cb68b27 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4127,7 +4127,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon) BT_DBG("hcon %p", hcon); - if (hcon->type != ACL_LINK || !conn) + if ((hcon->type != ACL_LINK && hcon->type != LE_LINK) || !conn) return 0x13; return conn->disc_reason; -- cgit v1.1 From eb492e0169974ac6d168f11d1fc1e2753fe1f3b4 Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 9 Jun 2011 18:50:40 -0300 Subject: Bluetooth: Implement the first SMP commands These simple commands will allow the SMP procedure to be started and terminated with a not supported error. This is the first step toward something useful. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Anderson Briglia Signed-off-by: Gustavo F. Padovan --- net/bluetooth/Makefile | 2 +- net/bluetooth/smp.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 net/bluetooth/smp.c (limited to 'net') diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index f04fe9a..9b67f3d 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -9,5 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o -bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o +bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o smp.o bluetooth-$(CONFIG_BT_SCO) += sco.o diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c new file mode 100644 index 0000000..aa0434f --- /dev/null +++ b/net/bluetooth/smp.c @@ -0,0 +1,146 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation; + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + SOFTWARE IS DISCLAIMED. +*/ + +#include +#include +#include +#include + +static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, + u16 dlen, void *data) +{ + struct sk_buff *skb; + struct l2cap_hdr *lh; + int len; + + len = L2CAP_HDR_SIZE + sizeof(code) + dlen; + + if (len > conn->mtu) + return NULL; + + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) + return NULL; + + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->len = cpu_to_le16(sizeof(code) + dlen); + lh->cid = cpu_to_le16(L2CAP_CID_SMP); + + memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code)); + + memcpy(skb_put(skb, dlen), data, dlen); + + return skb; +} + +static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) +{ + struct sk_buff *skb = smp_build_cmd(conn, code, len, data); + + BT_DBG("code 0x%2.2x", code); + + if (!skb) + return; + + hci_send_acl(conn->hcon, skb, 0); +} + +int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) +{ + __u8 authreq; + + BT_DBG("conn %p hcon %p level 0x%2.2x", conn, conn->hcon, sec_level); + + switch (sec_level) { + case BT_SECURITY_MEDIUM: + /* Encrypted, no MITM protection */ + authreq = HCI_AT_NO_BONDING_MITM; + break; + + case BT_SECURITY_HIGH: + /* Bonding, MITM protection */ + authreq = HCI_AT_GENERAL_BONDING_MITM; + break; + + case BT_SECURITY_LOW: + default: + return 1; + } + + if (conn->hcon->link_mode & HCI_LM_MASTER) { + struct smp_cmd_pairing cp; + cp.io_capability = 0x00; + cp.oob_flag = 0x00; + cp.max_key_size = 16; + cp.init_key_dist = 0x00; + cp.resp_key_dist = 0x00; + cp.auth_req = authreq; + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); + } else { + struct smp_cmd_security_req cp; + cp.auth_req = authreq; + smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp); + } + + return 0; +} + +int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) +{ + __u8 code = skb->data[0]; + __u8 reason; + int err = 0; + + skb_pull(skb, sizeof(code)); + + switch (code) { + case SMP_CMD_PAIRING_REQ: + reason = SMP_PAIRING_NOTSUPP; + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), + &reason); + err = -EOPNOTSUPP; + break; + + case SMP_CMD_PAIRING_FAIL: + break; + + case SMP_CMD_PAIRING_RSP: + case SMP_CMD_PAIRING_CONFIRM: + case SMP_CMD_PAIRING_RANDOM: + case SMP_CMD_ENCRYPT_INFO: + case SMP_CMD_MASTER_IDENT: + case SMP_CMD_IDENT_INFO: + case SMP_CMD_IDENT_ADDR_INFO: + case SMP_CMD_SIGN_INFO: + case SMP_CMD_SECURITY_REQ: + default: + BT_DBG("Unknown command code 0x%2.2x", code); + + reason = SMP_CMD_NOTSUPP; + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), + &reason); + err = -EOPNOTSUPP; + } + + kfree_skb(skb); + return err; +} -- cgit v1.1 From b501d6a1dc21eb61cbbc54e40780084f5d517164 Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Tue, 7 Jun 2011 18:46:31 -0300 Subject: Bluetooth: Start SMP procedure Start SMP procedure for LE connections. This modification intercepts l2cap received frames and call proper SMP functions to start the SMP procedure. By now, no keys are being used. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Anderson Briglia Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index cb68b27..8657165 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -54,6 +54,7 @@ #include #include #include +#include int disable_ertm; @@ -909,12 +910,15 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) __clear_chan_timer(chan); l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); + if (smp_conn_security(conn, chan->sec_level)) + BT_DBG("Insufficient security"); } if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { __clear_chan_timer(chan); l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); + } else if (chan->state == BT_CONNECT) l2cap_do_start(chan); @@ -4060,6 +4064,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) l2cap_att_channel(conn, cid, skb); break; + case L2CAP_CID_SMP: + if (smp_sig_channel(conn, skb)) + l2cap_conn_del(conn->hcon, EACCES); + break; + default: l2cap_data_channel(conn, cid, skb); break; -- cgit v1.1 From 88ba43b662b6b944c6278ad81a114fa559807776 Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 9 Jun 2011 18:50:42 -0300 Subject: Bluetooth: Add simple SMP pairing negotiation This implementation only exchanges SMP messages between the Host and the Remote. No keys are being generated. TK and STK generation will be provided in further patches. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 102 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index aa0434f..2240e96 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -64,6 +64,94 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) hci_send_acl(conn->hcon, skb, 0); } +static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_pairing *rp = (void *) skb->data; + + BT_DBG("conn %p", conn); + + skb_pull(skb, sizeof(*rp)); + + rp->io_capability = 0x00; + rp->oob_flag = 0x00; + rp->max_key_size = 16; + rp->init_key_dist = 0x00; + rp->resp_key_dist = 0x00; + rp->auth_req &= (SMP_AUTH_BONDING | SMP_AUTH_MITM); + + smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp); +} + +static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_pairing_confirm cp; + + BT_DBG("conn %p", conn); + + memset(&cp, 0, sizeof(cp)); + + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); +} + +static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, + struct sk_buff *skb) +{ + BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); + + if (conn->hcon->out) { + struct smp_cmd_pairing_random random; + + memset(&random, 0, sizeof(random)); + + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), + &random); + } else { + struct smp_cmd_pairing_confirm confirm; + + memset(&confirm, 0, sizeof(confirm)); + + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(confirm), + &confirm); + } +} + +static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_pairing_random cp; + + BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); + + skb_pull(skb, sizeof(cp)); + + if (conn->hcon->out) { + /* FIXME: start encryption */ + } else { + memset(&cp, 0, sizeof(cp)); + + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(cp), &cp); + } +} + +static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) +{ + struct smp_cmd_security_req *rp = (void *) skb->data; + struct smp_cmd_pairing cp; + + BT_DBG("conn %p", conn); + + skb_pull(skb, sizeof(*rp)); + memset(&cp, 0, sizeof(cp)); + + cp.io_capability = 0x00; + cp.oob_flag = 0x00; + cp.max_key_size = 16; + cp.init_key_dist = 0x00; + cp.resp_key_dist = 0x00; + cp.auth_req = rp->auth_req & (SMP_AUTH_BONDING | SMP_AUTH_MITM); + + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); +} + int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) { __u8 authreq; @@ -114,24 +202,33 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) switch (code) { case SMP_CMD_PAIRING_REQ: - reason = SMP_PAIRING_NOTSUPP; - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), - &reason); - err = -EOPNOTSUPP; + smp_cmd_pairing_req(conn, skb); break; case SMP_CMD_PAIRING_FAIL: break; case SMP_CMD_PAIRING_RSP: + smp_cmd_pairing_rsp(conn, skb); + break; + + case SMP_CMD_SECURITY_REQ: + smp_cmd_security_req(conn, skb); + break; + case SMP_CMD_PAIRING_CONFIRM: + smp_cmd_pairing_confirm(conn, skb); + break; + case SMP_CMD_PAIRING_RANDOM: + smp_cmd_pairing_random(conn, skb); + break; + case SMP_CMD_ENCRYPT_INFO: case SMP_CMD_MASTER_IDENT: case SMP_CMD_IDENT_INFO: case SMP_CMD_IDENT_ADDR_INFO: case SMP_CMD_SIGN_INFO: - case SMP_CMD_SECURITY_REQ: default: BT_DBG("Unknown command code 0x%2.2x", code); -- cgit v1.1 From 3a0259bb80cec7595a2d085a150412d23ba28c81 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:43 -0300 Subject: Bluetooth: Add support for using the crypto subsystem This will allow using the crypto subsystem for encrypting data. As SMP (Security Manager Protocol) is implemented almost entirely on the host side and the crypto module already implements the needed methods (AES-128), it makes sense to use it. There's now a new module option to enable/disable SMP support. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Anderson Briglia Signed-off-by: Gustavo F. Padovan --- net/bluetooth/Kconfig | 8 ++++++++ net/bluetooth/hci_core.c | 22 ++++++++++++++++++++++ net/bluetooth/smp.c | 22 ++++++++++++++++++---- 3 files changed, 48 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 6ae5ec5..f495dea 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -22,6 +22,7 @@ menuconfig BT BNEP Module (Bluetooth Network Encapsulation Protocol) CMTP Module (CAPI Message Transport Protocol) HIDP Module (Human Interface Device Protocol) + SMP Module (Security Manager Protocol) Say Y here to compile Bluetooth support into the kernel or say M to compile it as module (bluetooth). @@ -36,11 +37,18 @@ if BT != n config BT_L2CAP bool "L2CAP protocol support" select CRC16 + select CRYPTO + select CRYPTO_BLKCIPHER + select CRYPTO_AES + select CRYPTO_ECB help L2CAP (Logical Link Control and Adaptation Protocol) provides connection oriented and connection-less data transport. L2CAP support is required for most Bluetooth applications. + Also included is support for SMP (Security Manager Protocol) which + is the security layer on top of LE (Low Energy) links. + config BT_SCO bool "SCO links support" help diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e14e8a1..f62ca19 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,8 @@ static void hci_tx_task(unsigned long arg); static DEFINE_RWLOCK(hci_task_lock); +static int enable_smp; + /* HCI device list */ LIST_HEAD(hci_dev_list); DEFINE_RWLOCK(hci_dev_list_lock); @@ -1274,6 +1277,14 @@ int hci_add_adv_entry(struct hci_dev *hdev, return 0; } +static struct crypto_blkcipher *alloc_cypher(void) +{ + if (enable_smp) + return crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + + return ERR_PTR(-ENOTSUPP); +} + /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1358,6 +1369,11 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->workqueue) goto nomem; + hdev->tfm = alloc_cypher(); + if (IS_ERR(hdev->tfm)) + BT_INFO("Failed to load transform for ecb(aes): %ld", + PTR_ERR(hdev->tfm)); + hci_register_sysfs(hdev); hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, @@ -1406,6 +1422,9 @@ int hci_unregister_dev(struct hci_dev *hdev) !test_bit(HCI_SETUP, &hdev->flags)) mgmt_index_removed(hdev->id); + if (!IS_ERR(hdev->tfm)) + crypto_free_blkcipher(hdev->tfm); + hci_notify(hdev, HCI_DEV_UNREG); if (hdev->rfkill) { @@ -2242,3 +2261,6 @@ static void hci_cmd_task(unsigned long arg) } } } + +module_param(enable_smp, bool, 0644); +MODULE_PARM_DESC(enable_smp, "Enable SMP support (LE only)"); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 2240e96..aa20bee 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -154,9 +154,13 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) { + struct hci_conn *hcon = conn->hcon; __u8 authreq; - BT_DBG("conn %p hcon %p level 0x%2.2x", conn, conn->hcon, sec_level); + BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); + + if (IS_ERR(hcon->hdev->tfm)) + return 1; switch (sec_level) { case BT_SECURITY_MEDIUM: @@ -174,7 +178,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) return 1; } - if (conn->hcon->link_mode & HCI_LM_MASTER) { + if (hcon->link_mode & HCI_LM_MASTER) { struct smp_cmd_pairing cp; cp.io_capability = 0x00; cp.oob_flag = 0x00; @@ -198,6 +202,12 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) __u8 reason; int err = 0; + if (IS_ERR(conn->hcon->hdev->tfm)) { + err = PTR_ERR(conn->hcon->hdev->tfm); + reason = SMP_PAIRING_NOTSUPP; + goto done; + } + skb_pull(skb, sizeof(code)); switch (code) { @@ -233,11 +243,15 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("Unknown command code 0x%2.2x", code); reason = SMP_CMD_NOTSUPP; - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), - &reason); err = -EOPNOTSUPP; + goto done; } +done: + if (reason) + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), + &reason); + kfree_skb(skb); return err; } -- cgit v1.1 From d22ef0bc83c5a4e7ca9f2791e658dc8949d31ff4 Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 9 Jun 2011 18:50:44 -0300 Subject: Bluetooth: Add LE SMP Cryptoolbox functions This patch implements SMP crypto functions called ah, c1, s1 and e. It also implements auxiliary functions. All These functions are needed for SMP keys generation. Signed-off-by: Anderson Briglia Signed-off-by: Anderson Lizardo Signed-off-by: Bruna Moreira Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index aa20bee..57fc7d0 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -24,6 +24,123 @@ #include #include #include +#include +#include + +static inline void swap128(u8 src[16], u8 dst[16]) +{ + int i; + for (i = 0; i < 16; i++) + dst[15 - i] = src[i]; +} + +static inline void swap56(u8 src[7], u8 dst[7]) +{ + int i; + for (i = 0; i < 7; i++) + dst[6 - i] = src[i]; +} + +static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) +{ + struct blkcipher_desc desc; + struct scatterlist sg; + int err, iv_len; + unsigned char iv[128]; + + if (tfm == NULL) { + BT_ERR("tfm %p", tfm); + return -EINVAL; + } + + desc.tfm = tfm; + desc.flags = 0; + + err = crypto_blkcipher_setkey(tfm, k, 16); + if (err) { + BT_ERR("cipher setkey failed: %d", err); + return err; + } + + sg_init_one(&sg, r, 16); + + iv_len = crypto_blkcipher_ivsize(tfm); + if (iv_len) { + memset(&iv, 0xff, iv_len); + crypto_blkcipher_set_iv(tfm, iv, iv_len); + } + + err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16); + if (err) + BT_ERR("Encrypt data error %d", err); + + return err; +} + +static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16], + u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, + u8 _rat, bdaddr_t *ra, u8 res[16]) +{ + u8 p1[16], p2[16]; + int err; + + memset(p1, 0, 16); + + /* p1 = pres || preq || _rat || _iat */ + swap56(pres, p1); + swap56(preq, p1 + 7); + p1[14] = _rat; + p1[15] = _iat; + + memset(p2, 0, 16); + + /* p2 = padding || ia || ra */ + baswap((bdaddr_t *) (p2 + 4), ia); + baswap((bdaddr_t *) (p2 + 10), ra); + + /* res = r XOR p1 */ + u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); + + /* res = e(k, res) */ + err = smp_e(tfm, k, res); + if (err) { + BT_ERR("Encrypt data error"); + return err; + } + + /* res = res XOR p2 */ + u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); + + /* res = e(k, res) */ + err = smp_e(tfm, k, res); + if (err) + BT_ERR("Encrypt data error"); + + return err; +} + +static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], + u8 r1[16], u8 r2[16], u8 _r[16]) +{ + int err; + + /* Just least significant octets from r1 and r2 are considered */ + memcpy(_r, r1 + 8, 8); + memcpy(_r + 8, r2 + 8, 8); + + err = smp_e(tfm, k, _r); + if (err) + BT_ERR("Encrypt data error"); + + return err; +} + +static int smp_rand(u8 *buf) +{ + get_random_bytes(buf, 16); + + return 0; +} static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, u16 dlen, void *data) -- cgit v1.1 From f01ead315785768cdb6e928646f90a47640bcdd9 Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 9 Jun 2011 18:50:45 -0300 Subject: Bluetooth: Add SMP confirmation structs This patch adds initial support for verifying the confirmation value that the remote side has sent. Signed-off-by: Anderson Briglia Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 57fc7d0..fa22f4a 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -187,6 +187,8 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); + conn->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&conn->preq[1], rp, sizeof(*rp)); skb_pull(skb, sizeof(*rp)); rp->io_capability = 0x00; @@ -196,17 +198,25 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) rp->resp_key_dist = 0x00; rp->auth_req &= (SMP_AUTH_BONDING | SMP_AUTH_MITM); + conn->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&conn->prsp[1], rp, sizeof(*rp)); + smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp); } static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { + struct smp_cmd_pairing *rp = (void *) skb->data; struct smp_cmd_pairing_confirm cp; BT_DBG("conn %p", conn); memset(&cp, 0, sizeof(cp)); + conn->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&conn->prsp[1], rp, sizeof(*rp)); + skb_pull(skb, sizeof(*rp)); + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); } @@ -266,6 +276,9 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) cp.resp_key_dist = 0x00; cp.auth_req = rp->auth_req & (SMP_AUTH_BONDING | SMP_AUTH_MITM); + conn->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&conn->preq[1], &cp, sizeof(cp)); + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } @@ -303,6 +316,10 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) cp.init_key_dist = 0x00; cp.resp_key_dist = 0x00; cp.auth_req = authreq; + + conn->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&conn->preq[1], &cp, sizeof(cp)); + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } else { struct smp_cmd_security_req cp; -- cgit v1.1 From 7d24ddcc1140d2f796436e476c8d69469610588b Mon Sep 17 00:00:00 2001 From: Anderson Briglia Date: Thu, 9 Jun 2011 18:50:46 -0300 Subject: Bluetooth: Add SMP confirmation checks methods This patch includes support for generating and sending the random value used to produce the confirmation value. Signed-off-by: Anderson Briglia Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 97 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index fa22f4a..7a9a195 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -198,6 +198,9 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) rp->resp_key_dist = 0x00; rp->auth_req &= (SMP_AUTH_BONDING | SMP_AUTH_MITM); + /* Just works */ + memset(conn->tk, 0, sizeof(conn->tk)); + conn->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&conn->prsp[1], rp, sizeof(*rp)); @@ -208,54 +211,120 @@ static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *rp = (void *) skb->data; struct smp_cmd_pairing_confirm cp; + struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + int ret; + u8 res[16]; BT_DBG("conn %p", conn); - memset(&cp, 0, sizeof(cp)); + /* Just works */ + memset(conn->tk, 0, sizeof(conn->tk)); conn->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&conn->prsp[1], rp, sizeof(*rp)); skb_pull(skb, sizeof(*rp)); + ret = smp_rand(conn->prnd); + if (ret) + return; + + ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0, + conn->src, conn->hcon->dst_type, conn->dst, res); + if (ret) + return; + + swap128(res, cp.confirm_val); + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); } static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { + struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - if (conn->hcon->out) { - struct smp_cmd_pairing_random random; + memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf)); + skb_pull(skb, sizeof(conn->pcnf)); - memset(&random, 0, sizeof(random)); + if (conn->hcon->out) { + u8 random[16]; + swap128(conn->prnd, random); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), - &random); + random); } else { - struct smp_cmd_pairing_confirm confirm; + struct smp_cmd_pairing_confirm cp; + int ret; + u8 res[16]; - memset(&confirm, 0, sizeof(confirm)); + ret = smp_rand(conn->prnd); + if (ret) + return; - smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(confirm), - &confirm); + ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, + conn->hcon->dst_type, conn->dst, + 0, conn->src, res); + if (ret) + return; + + swap128(res, cp.confirm_val); + + smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); } } static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { - struct smp_cmd_pairing_random cp; + struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + int ret; + u8 key[16], res[16], random[16], confirm[16], buf[128]; + + swap128(skb->data, random); + skb_pull(skb, sizeof(random)); + + if (conn->hcon->out) + ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0, + conn->src, conn->hcon->dst_type, conn->dst, + res); + else + ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, + conn->hcon->dst_type, conn->dst, 0, conn->src, + res); + if (ret) + return; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - skb_pull(skb, sizeof(cp)); + swap128(res, confirm); + + if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) { + struct smp_cmd_pairing_fail cp; + + BT_ERR("Pairing failed (confirmation values mismatch)"); + cp.reason = SMP_CONFIRM_FAILED; + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(cp), &cp); + return; + } if (conn->hcon->out) { - /* FIXME: start encryption */ + smp_s1(tfm, conn->tk, random, conn->prnd, key); + + hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, + sizeof(buf), 0); + BT_DBG("key %s", buf); } else { - memset(&cp, 0, sizeof(cp)); + u8 r[16]; + + swap128(conn->prnd, r); + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); + + smp_s1(tfm, conn->tk, conn->prnd, random, key); - smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(cp), &cp); + hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, + sizeof(buf), 0); + BT_DBG("key %s", buf); } } -- cgit v1.1 From a7a595f675f1b33dc73167147321dba5c4395acc Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:47 -0300 Subject: Bluetooth: Add support for LE Start Encryption This adds support for starting SMP Phase 2 Encryption, when the initial SMP negotiation is successful. This adds the LE Start Encryption and LE Long Term Key Request commands and related events. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 49 ++++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/smp.c | 16 ++++++++--- 3 files changed, 128 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 37f5a17..1819383 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -204,6 +204,55 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, } EXPORT_SYMBOL(hci_le_conn_update); +void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], + __u8 ltk[16]) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_start_enc cp; + + BT_DBG("%p", conn); + + memset(&cp, 0, sizeof(cp)); + + cp.handle = cpu_to_le16(conn->handle); + memcpy(cp.ltk, ltk, sizeof(cp.ltk)); + cp.ediv = ediv; + memcpy(cp.rand, rand, sizeof(rand)); + + hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp); +} +EXPORT_SYMBOL(hci_le_start_enc); + +void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_ltk_reply cp; + + BT_DBG("%p", conn); + + memset(&cp, 0, sizeof(cp)); + + cp.handle = cpu_to_le16(conn->handle); + memcpy(cp.ltk, ltk, sizeof(ltk)); + + hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); +} +EXPORT_SYMBOL(hci_le_ltk_reply); + +void hci_le_ltk_neg_reply(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_ltk_neg_reply cp; + + BT_DBG("%p", conn); + + memset(&cp, 0, sizeof(cp)); + + cp.handle = cpu_to_le16(conn->handle); + + hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(cp), &cp); +} + /* Device _must_ be locked */ void hci_sco_setup(struct hci_conn *conn, __u8 status) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0f643f8..166fa11 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -868,6 +868,30 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, hci_dev_unlock(hdev); } +static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_le_ltk_reply *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status); +} + +static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1248,6 +1272,11 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) hci_dev_unlock(hdev); } +static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) +{ + BT_DBG("%s status 0x%x", hdev->name, status); +} + static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -1856,6 +1885,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_le_set_scan_enable(hdev, skb); break; + case HCI_OP_LE_LTK_REPLY: + hci_cc_le_ltk_reply(hdev, skb); + break; + + case HCI_OP_LE_LTK_NEG_REPLY: + hci_cc_le_ltk_neg_reply(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -1934,6 +1971,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_le_create_conn(hdev, ev->status); break; + case HCI_OP_LE_START_ENC: + hci_cs_le_start_enc(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -2745,6 +2786,28 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev, hci_dev_unlock(hdev); } +static inline void hci_le_ltk_request_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_le_ltk_req *ev = (void *) skb->data; + struct hci_cp_le_ltk_reply cp; + struct hci_conn *conn; + + BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle)); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + + memset(&cp, 0, sizeof(cp)); + cp.handle = cpu_to_le16(conn->handle); + memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk)); + + hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); + + hci_dev_unlock(hdev); +} + static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_meta *le_ev = (void *) skb->data; @@ -2760,6 +2823,10 @@ static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_le_adv_report_evt(hdev, skb); break; + case HCI_EV_LE_LTK_REQ: + hci_le_ltk_request_evt(hdev, skb); + break; + default: break; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 7a9a195..56828db 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -277,13 +277,16 @@ static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { - struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + struct hci_conn *hcon = conn->hcon; + struct crypto_blkcipher *tfm = hcon->hdev->tfm; int ret; u8 key[16], res[16], random[16], confirm[16], buf[128]; swap128(skb->data, random); skb_pull(skb, sizeof(random)); + memset(hcon->ltk, 0, sizeof(hcon->ltk)); + if (conn->hcon->out) ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0, conn->src, conn->hcon->dst_type, conn->dst, @@ -309,11 +312,15 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) } if (conn->hcon->out) { + __le16 ediv; + u8 rand[8]; + smp_s1(tfm, conn->tk, random, conn->prnd, key); + swap128(key, hcon->ltk); - hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, - sizeof(buf), 0); - BT_DBG("key %s", buf); + memset(rand, 0, sizeof(rand)); + ediv = 0; + hci_le_start_enc(hcon, ediv, rand, hcon->ltk); } else { u8 r[16]; @@ -321,6 +328,7 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); smp_s1(tfm, conn->tk, conn->prnd, random, key); + swap128(key, hcon->ltk); hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, sizeof(buf), 0); -- cgit v1.1 From 9b3d67405b17d61ba8be9d824222fb410f487b8a Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:48 -0300 Subject: Bluetooth: Remove debug statements Now that these commands are sent to the controller we can use hcidump to verify that the correct values are produced. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 56828db..6983979 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -280,7 +280,7 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) struct hci_conn *hcon = conn->hcon; struct crypto_blkcipher *tfm = hcon->hdev->tfm; int ret; - u8 key[16], res[16], random[16], confirm[16], buf[128]; + u8 key[16], res[16], random[16], confirm[16]; swap128(skb->data, random); skb_pull(skb, sizeof(random)); @@ -329,10 +329,6 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) smp_s1(tfm, conn->tk, conn->prnd, random, key); swap128(key, hcon->ltk); - - hex_dump_to_buffer(key, sizeof(key), 16, 1, buf, - sizeof(buf), 0); - BT_DBG("key %s", buf); } } -- cgit v1.1 From f1cb9af557dd8fb5d98fbcc4b5d3eb9d6d235af7 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Wed, 26 Jan 2011 21:42:57 -0300 Subject: Bluetooth: Add support for resuming socket when SMP is finished This adds support for resuming the user space traffic when SMP negotiation is complete. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 61 +++++++++++++++++++++++----------------------- net/bluetooth/l2cap_sock.c | 16 ++++++++++++ net/bluetooth/smp.c | 40 +++++++++++++++++++++--------- 3 files changed, 75 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8657165..584a423 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -890,6 +890,23 @@ clean: bh_unlock_sock(parent); } +static void l2cap_chan_ready(struct sock *sk) +{ + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + struct sock *parent = bt_sk(sk)->parent; + + BT_DBG("sk %p, parent %p", sk, parent); + + chan->conf_state = 0; + __clear_chan_timer(chan); + + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); + + if (parent) + parent->sk_data_ready(parent, 0); +} + static void l2cap_conn_ready(struct l2cap_conn *conn) { struct l2cap_chan *chan; @@ -906,13 +923,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) bh_lock_sock(sk); - if (conn->hcon->type == LE_LINK) { - __clear_chan_timer(chan); - l2cap_state_change(chan, BT_CONNECTED); - sk->sk_state_change(sk); + if (conn->hcon->type == LE_LINK) if (smp_conn_security(conn, chan->sec_level)) - BT_DBG("Insufficient security"); - } + l2cap_chan_ready(sk); if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { __clear_chan_timer(chan); @@ -1675,30 +1688,6 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) return err; } -static void l2cap_chan_ready(struct sock *sk) -{ - struct sock *parent = bt_sk(sk)->parent; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - - BT_DBG("sk %p, parent %p", sk, parent); - - chan->conf_state = 0; - __clear_chan_timer(chan); - - if (!parent) { - /* Outgoing channel. - * Wake up socket sleeping on connect. - */ - l2cap_state_change(chan, BT_CONNECTED); - sk->sk_state_change(sk); - } else { - /* Incoming channel. - * Wake up socket sleeping on accept. - */ - parent->sk_data_ready(parent, 0); - } -} - /* Copy frame to all raw sockets on that connection */ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) { @@ -4188,6 +4177,18 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) bh_lock_sock(sk); + BT_DBG("chan->scid %d", chan->scid); + + if (chan->scid == L2CAP_CID_LE_DATA) { + if (!status && encrypt) { + chan->sec_level = hcon->sec_level; + l2cap_chan_ready(sk); + } + + bh_unlock_sock(sk); + continue; + } + if (chan->conf_state & L2CAP_CONF_CONNECT_PEND) { bh_unlock_sock(sk); continue; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 1d9c365..5c819e0 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -29,6 +29,7 @@ #include #include #include +#include static const struct proto_ops l2cap_sock_ops; static void l2cap_sock_init(struct sock *sk, struct sock *parent); @@ -562,6 +563,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; struct bt_power pwr; + struct l2cap_conn *conn; int len, err = 0; u32 opt; @@ -598,6 +600,20 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch } chan->sec_level = sec.level; + + conn = chan->conn; + if (conn && chan->scid == L2CAP_CID_LE_DATA) { + if (!conn->hcon->out) { + err = -EINVAL; + break; + } + + if (smp_conn_security(conn, sec.level)) + break; + + err = 0; + sk->sk_state = BT_CONFIG; + } break; case BT_DEFER_SETUP: diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 6983979..da46d76 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -336,9 +336,13 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_security_req *rp = (void *) skb->data; struct smp_cmd_pairing cp; + struct hci_conn *hcon = conn->hcon; BT_DBG("conn %p", conn); + if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + return; + skb_pull(skb, sizeof(*rp)); memset(&cp, 0, sizeof(cp)); @@ -353,6 +357,20 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(&conn->preq[1], &cp, sizeof(cp)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); + + set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); +} + +static __u8 seclevel_to_authreq(__u8 level) +{ + switch (level) { + case BT_SECURITY_HIGH: + /* For now we don't support bonding */ + return SMP_AUTH_MITM; + + default: + return SMP_AUTH_NONE; + } } int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) @@ -365,21 +383,16 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) if (IS_ERR(hcon->hdev->tfm)) return 1; - switch (sec_level) { - case BT_SECURITY_MEDIUM: - /* Encrypted, no MITM protection */ - authreq = HCI_AT_NO_BONDING_MITM; - break; + if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + return 0; - case BT_SECURITY_HIGH: - /* Bonding, MITM protection */ - authreq = HCI_AT_GENERAL_BONDING_MITM; - break; + if (sec_level == BT_SECURITY_LOW) + return 1; - case BT_SECURITY_LOW: - default: + if (hcon->sec_level >= sec_level) return 1; - } + + authreq = seclevel_to_authreq(sec_level); if (hcon->link_mode & HCI_LM_MASTER) { struct smp_cmd_pairing cp; @@ -400,6 +413,9 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp); } + hcon->pending_sec_level = sec_level; + set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); + return 0; } -- cgit v1.1 From 7b5c0d5242295a3b52e7161bf129e2f0e8c624cb Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:50 -0300 Subject: Bluetooth: Fix initial security level of LE links As the default security level (BT_SECURITY_SDP) doesn't make sense for LE links, initialize LE links with something that makes sense. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 1 + net/bluetooth/hci_event.c | 1 + 2 files changed, 2 insertions(+) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 1819383..2f5ae53 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -53,6 +53,7 @@ static void hci_le_connect(struct hci_conn *conn) conn->state = BT_CONNECT; conn->out = 1; conn->link_mode |= HCI_LM_MASTER; + conn->sec_level = BT_SECURITY_LOW; memset(&cp, 0, sizeof(cp)); cp.scan_interval = cpu_to_le16(0x0004); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 166fa11..21d6055 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2753,6 +2753,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff mgmt_connected(hdev->id, &ev->bdaddr); + conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; -- cgit v1.1 From b8e66eacab21870d4f800822111c494f9ef291e3 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:52 -0300 Subject: Bluetooth: Add support for building pairing commands Before we are able to do a proper exchange of pairing parameters, we need a unified way of building pairing requests and responses. For IO Capability we use the value that was set by userspace, using the management interface. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index da46d76..d29700d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -181,6 +181,17 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) hci_send_acl(conn->hcon, skb, 0); } +static void build_pairing_cmd(struct l2cap_conn *conn, + struct smp_cmd_pairing *cmd, __u8 authreq) +{ + cmd->io_capability = conn->hcon->io_capability; + cmd->oob_flag = SMP_OOB_NOT_PRESENT; + cmd->max_key_size = 16; + cmd->init_key_dist = 0x00; + cmd->resp_key_dist = 0x00; + cmd->auth_req = authreq; +} + static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *rp = (void *) skb->data; -- cgit v1.1 From da85e5e5afeb72bb6e6b5192a2d252861fafc3e7 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:53 -0300 Subject: Bluetooth: Add support for Pairing features exchange This patch implements a simple version of the SMP Pairing Features exchange procedure (Vol. 3 Part H, Section 2.3.5.1). For now, everything that would cause a Pairing Method different of Just Works to be chosen is rejected. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 1 + net/bluetooth/smp.c | 106 +++++++++++++++++++++++----------------------- 2 files changed, 54 insertions(+), 53 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 21d6055..ac2c5e8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1622,6 +1622,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff * /* Encryption implies authentication */ conn->link_mode |= HCI_LM_AUTH; conn->link_mode |= HCI_LM_ENCRYPT; + conn->sec_level = conn->pending_sec_level; } else conn->link_mode &= ~HCI_LM_ENCRYPT; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index d29700d..dfd6891 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -181,6 +181,18 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) hci_send_acl(conn->hcon, skb, 0); } +static __u8 seclevel_to_authreq(__u8 level) +{ + switch (level) { + case BT_SECURITY_HIGH: + /* Right now we don't support bonding */ + return SMP_AUTH_MITM; + + default: + return SMP_AUTH_NONE; + } +} + static void build_pairing_cmd(struct l2cap_conn *conn, struct smp_cmd_pairing *cmd, __u8 authreq) { @@ -192,7 +204,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, cmd->auth_req = authreq; } -static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) +static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *rp = (void *) skb->data; @@ -202,12 +214,11 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(&conn->preq[1], rp, sizeof(*rp)); skb_pull(skb, sizeof(*rp)); - rp->io_capability = 0x00; - rp->oob_flag = 0x00; - rp->max_key_size = 16; - rp->init_key_dist = 0x00; - rp->resp_key_dist = 0x00; - rp->auth_req &= (SMP_AUTH_BONDING | SMP_AUTH_MITM); + if (rp->oob_flag) + return SMP_OOB_NOT_AVAIL; + + /* We didn't start the pairing, so no requirements */ + build_pairing_cmd(conn, rp, SMP_AUTH_NONE); /* Just works */ memset(conn->tk, 0, sizeof(conn->tk)); @@ -216,9 +227,11 @@ static void smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(&conn->prsp[1], rp, sizeof(*rp)); smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp); + + return 0; } -static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) +static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *rp = (void *) skb->data; struct smp_cmd_pairing_confirm cp; @@ -228,29 +241,34 @@ static void smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); + skb_pull(skb, sizeof(*rp)); + + if (rp->oob_flag) + return SMP_OOB_NOT_AVAIL; + /* Just works */ memset(conn->tk, 0, sizeof(conn->tk)); conn->prsp[0] = SMP_CMD_PAIRING_RSP; memcpy(&conn->prsp[1], rp, sizeof(*rp)); - skb_pull(skb, sizeof(*rp)); ret = smp_rand(conn->prnd); if (ret) - return; + return SMP_UNSPECIFIED; ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0, conn->src, conn->hcon->dst_type, conn->dst, res); if (ret) - return; + return SMP_UNSPECIFIED; swap128(res, cp.confirm_val); smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + + return 0; } -static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, - struct sk_buff *skb) +static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; @@ -272,21 +290,23 @@ static void smp_cmd_pairing_confirm(struct l2cap_conn *conn, ret = smp_rand(conn->prnd); if (ret) - return; + return SMP_UNSPECIFIED; ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, conn->hcon->dst_type, conn->dst, 0, conn->src, res); if (ret) - return; + return SMP_CONFIRM_FAILED; swap128(res, cp.confirm_val); smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); } + + return 0; } -static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) +static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { struct hci_conn *hcon = conn->hcon; struct crypto_blkcipher *tfm = hcon->hdev->tfm; @@ -307,19 +327,15 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) conn->hcon->dst_type, conn->dst, 0, conn->src, res); if (ret) - return; + return SMP_UNSPECIFIED; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); swap128(res, confirm); if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) { - struct smp_cmd_pairing_fail cp; - BT_ERR("Pairing failed (confirmation values mismatch)"); - cp.reason = SMP_CONFIRM_FAILED; - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(cp), &cp); - return; + return SMP_CONFIRM_FAILED; } if (conn->hcon->out) { @@ -341,9 +357,11 @@ static void smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) smp_s1(tfm, conn->tk, conn->prnd, random, key); swap128(key, hcon->ltk); } + + return 0; } -static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) +static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_security_req *rp = (void *) skb->data; struct smp_cmd_pairing cp; @@ -352,17 +370,12 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("conn %p", conn); if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) - return; + return 0; skb_pull(skb, sizeof(*rp)); - memset(&cp, 0, sizeof(cp)); - cp.io_capability = 0x00; - cp.oob_flag = 0x00; - cp.max_key_size = 16; - cp.init_key_dist = 0x00; - cp.resp_key_dist = 0x00; - cp.auth_req = rp->auth_req & (SMP_AUTH_BONDING | SMP_AUTH_MITM); + memset(&cp, 0, sizeof(cp)); + build_pairing_cmd(conn, &cp, rp->auth_req); conn->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&conn->preq[1], &cp, sizeof(cp)); @@ -370,18 +383,8 @@ static void smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); -} -static __u8 seclevel_to_authreq(__u8 level) -{ - switch (level) { - case BT_SECURITY_HIGH: - /* For now we don't support bonding */ - return SMP_AUTH_MITM; - - default: - return SMP_AUTH_NONE; - } + return 0; } int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) @@ -407,13 +410,8 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) if (hcon->link_mode & HCI_LM_MASTER) { struct smp_cmd_pairing cp; - cp.io_capability = 0x00; - cp.oob_flag = 0x00; - cp.max_key_size = 16; - cp.init_key_dist = 0x00; - cp.resp_key_dist = 0x00; - cp.auth_req = authreq; + build_pairing_cmd(conn, &cp, authreq); conn->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&conn->preq[1], &cp, sizeof(cp)); @@ -446,26 +444,28 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) switch (code) { case SMP_CMD_PAIRING_REQ: - smp_cmd_pairing_req(conn, skb); + reason = smp_cmd_pairing_req(conn, skb); break; case SMP_CMD_PAIRING_FAIL: + reason = 0; + err = -EPERM; break; case SMP_CMD_PAIRING_RSP: - smp_cmd_pairing_rsp(conn, skb); + reason = smp_cmd_pairing_rsp(conn, skb); break; case SMP_CMD_SECURITY_REQ: - smp_cmd_security_req(conn, skb); + reason = smp_cmd_security_req(conn, skb); break; case SMP_CMD_PAIRING_CONFIRM: - smp_cmd_pairing_confirm(conn, skb); + reason = smp_cmd_pairing_confirm(conn, skb); break; case SMP_CMD_PAIRING_RANDOM: - smp_cmd_pairing_random(conn, skb); + reason = smp_cmd_pairing_random(conn, skb); break; case SMP_CMD_ENCRYPT_INFO: -- cgit v1.1 From 94ac02726cb87dd569bac4536f33e4b696b692eb Mon Sep 17 00:00:00 2001 From: Anderson Lizardo Date: Mon, 13 Jun 2011 15:42:03 -0400 Subject: Bluetooth: fix missing parameter for HCI_OP_DISCONNECT The "dc" variable is initialized but not passed to hci_send_cmd(). Signed-off-by: Anderson Lizardo Signed-off-by: Bruna Moreira Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fcccf10..d192089 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -990,7 +990,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) put_unaligned_le16(conn->handle, &dc.handle); dc.reason = 0x13; /* Remote User Terminated Connection */ - err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL); + err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); } unlock: -- cgit v1.1 From df3c3931ec58cca3409c71b18ad6da0cd71fe163 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 14 Jun 2011 12:48:19 +0300 Subject: Bluetooth: Fix accepting connect requests for defer_setup When authentication completes we shouldn't blindly accept any pending L2CAP connect requests. If the socket has the defer_setup feature enabled it should still wait for user space acceptance of the connect request. The issue only happens for non-SSP connections since with SSP the L2CAP Connect request may not be sent for non-SDP PSMs before authentication has completed successfully. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e64a1c2..56fdd91 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4002,21 +4002,30 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) } } else if (sk->sk_state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; - __u16 result; + __u16 res, stat; if (!status) { - sk->sk_state = BT_CONFIG; - result = L2CAP_CR_SUCCESS; + if (bt_sk(sk)->defer_setup) { + struct sock *parent = bt_sk(sk)->parent; + res = L2CAP_CR_PEND; + stat = L2CAP_CS_AUTHOR_PEND; + parent->sk_data_ready(parent, 0); + } else { + sk->sk_state = BT_CONFIG; + res = L2CAP_CR_SUCCESS; + stat = L2CAP_CS_NO_INFO; + } } else { sk->sk_state = BT_DISCONN; l2cap_sock_set_timer(sk, HZ / 10); - result = L2CAP_CR_SEC_BLOCK; + res = L2CAP_CR_SEC_BLOCK; + stat = L2CAP_CS_NO_INFO; } rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + rsp.result = cpu_to_le16(res); + rsp.status = cpu_to_le16(stat); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); } -- cgit v1.1 From 5d3de7df18077a0f508ae2c3e3f1866da65fdffd Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Tue, 14 Jun 2011 13:37:41 -0300 Subject: Bluetooth: Add support for SMP timeout This patch adds support for disconnecting the link when SMP procedure takes more than 30 seconds. SMP begins when either the Pairing Request command is sent or the Pairing Response is received, and it ends when the link is encrypted (or terminated). Vol 3, Part H Section 3.4. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 74 +++++++++++++++++++++++++++------------------- net/bluetooth/smp.c | 14 +++++++++ 2 files changed, 58 insertions(+), 30 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 584a423..bbbae2e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -970,6 +970,45 @@ static void l2cap_info_timeout(unsigned long arg) l2cap_conn_start(conn); } +static void l2cap_conn_del(struct hci_conn *hcon, int err) +{ + struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_chan *chan, *l; + struct sock *sk; + + if (!conn) + return; + + BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); + + kfree_skb(conn->rx_skb); + + /* Kill channels */ + list_for_each_entry_safe(chan, l, &conn->chan_l, list) { + sk = chan->sk; + bh_lock_sock(sk); + l2cap_chan_del(chan, err); + bh_unlock_sock(sk); + chan->ops->close(chan->data); + } + + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) + del_timer_sync(&conn->info_timer); + + if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + del_timer(&conn->security_timer); + + hcon->l2cap_data = NULL; + kfree(conn); +} + +static void security_timeout(unsigned long arg) +{ + struct l2cap_conn *conn = (void *) arg; + + l2cap_conn_del(conn->hcon, ETIMEDOUT); +} + static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn = hcon->l2cap_data; @@ -1001,7 +1040,10 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) INIT_LIST_HEAD(&conn->chan_l); - if (hcon->type != LE_LINK) + if (hcon->type == LE_LINK) + setup_timer(&conn->security_timer, security_timeout, + (unsigned long) conn); + else setup_timer(&conn->info_timer, l2cap_info_timeout, (unsigned long) conn); @@ -1010,35 +1052,6 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) return conn; } -static void l2cap_conn_del(struct hci_conn *hcon, int err) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - struct l2cap_chan *chan, *l; - struct sock *sk; - - if (!conn) - return; - - BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); - - kfree_skb(conn->rx_skb); - - /* Kill channels */ - list_for_each_entry_safe(chan, l, &conn->chan_l, list) { - sk = chan->sk; - bh_lock_sock(sk); - l2cap_chan_del(chan, err); - bh_unlock_sock(sk); - chan->ops->close(chan->data); - } - - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) - del_timer_sync(&conn->info_timer); - - hcon->l2cap_data = NULL; - kfree(conn); -} - static inline void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { write_lock_bh(&conn->chan_lock); @@ -4182,6 +4195,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (chan->scid == L2CAP_CID_LE_DATA) { if (!status && encrypt) { chan->sec_level = hcon->sec_level; + del_timer(&conn->security_timer); l2cap_chan_ready(sk); } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index dfd6891..3988678 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -27,6 +27,8 @@ #include #include +#define SMP_TIMEOUT 30000 /* 30 seconds */ + static inline void swap128(u8 src[16], u8 dst[16]) { int i; @@ -228,6 +230,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp); + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); + return 0; } @@ -303,6 +308,9 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); } + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); + return 0; } @@ -382,6 +390,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); + set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); return 0; @@ -415,6 +426,9 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) conn->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&conn->preq[1], &cp, sizeof(cp)); + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); + smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } else { struct smp_cmd_security_req cp; -- cgit v1.1 From 3158c50c33c1acddcfa3c57fab812435aa459750 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Tue, 14 Jun 2011 13:37:42 -0300 Subject: Bluetooth: Add key size checks for SMP This patch implements a check in smp cmd pairing request and pairing response to verify if encryption key maximum size is compatible in both slave and master when SMP Pairing is requested. Keys are also masked to the correct negotiated size. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Anderson Briglia Signed-off-by: Gustavo F. Padovan --- net/bluetooth/smp.c | 54 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 3988678..52e9ec2 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -200,35 +200,51 @@ static void build_pairing_cmd(struct l2cap_conn *conn, { cmd->io_capability = conn->hcon->io_capability; cmd->oob_flag = SMP_OOB_NOT_PRESENT; - cmd->max_key_size = 16; + cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE; cmd->init_key_dist = 0x00; cmd->resp_key_dist = 0x00; cmd->auth_req = authreq; } +static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) +{ + if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || + (max_key_size < SMP_MIN_ENC_KEY_SIZE)) + return SMP_ENC_KEY_SIZE; + + conn->smp_key_size = max_key_size; + + return 0; +} + static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { - struct smp_cmd_pairing *rp = (void *) skb->data; + struct smp_cmd_pairing rsp, *req = (void *) skb->data; + u8 key_size; BT_DBG("conn %p", conn); conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], rp, sizeof(*rp)); - skb_pull(skb, sizeof(*rp)); + memcpy(&conn->preq[1], req, sizeof(*req)); + skb_pull(skb, sizeof(*req)); - if (rp->oob_flag) + if (req->oob_flag) return SMP_OOB_NOT_AVAIL; /* We didn't start the pairing, so no requirements */ - build_pairing_cmd(conn, rp, SMP_AUTH_NONE); + build_pairing_cmd(conn, &rsp, SMP_AUTH_NONE); + + key_size = min(req->max_key_size, rsp.max_key_size); + if (check_enc_key_size(conn, key_size)) + return SMP_ENC_KEY_SIZE; /* Just works */ memset(conn->tk, 0, sizeof(conn->tk)); conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], rp, sizeof(*rp)); + memcpy(&conn->prsp[1], &rsp, sizeof(rsp)); - smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(*rp), rp); + smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); mod_timer(&conn->security_timer, jiffies + msecs_to_jiffies(SMP_TIMEOUT)); @@ -238,24 +254,30 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { - struct smp_cmd_pairing *rp = (void *) skb->data; + struct smp_cmd_pairing *req, *rsp = (void *) skb->data; struct smp_cmd_pairing_confirm cp; struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; int ret; - u8 res[16]; + u8 res[16], key_size; BT_DBG("conn %p", conn); - skb_pull(skb, sizeof(*rp)); + skb_pull(skb, sizeof(*rsp)); + + req = (void *) &conn->preq[1]; - if (rp->oob_flag) + key_size = min(req->max_key_size, rsp->max_key_size); + if (check_enc_key_size(conn, key_size)) + return SMP_ENC_KEY_SIZE; + + if (rsp->oob_flag) return SMP_OOB_NOT_AVAIL; /* Just works */ memset(conn->tk, 0, sizeof(conn->tk)); conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], rp, sizeof(*rp)); + memcpy(&conn->prsp[1], rsp, sizeof(*rsp)); ret = smp_rand(conn->prnd); if (ret) @@ -353,6 +375,9 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) smp_s1(tfm, conn->tk, random, conn->prnd, key); swap128(key, hcon->ltk); + memset(hcon->ltk + conn->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + memset(rand, 0, sizeof(rand)); ediv = 0; hci_le_start_enc(hcon, ediv, rand, hcon->ltk); @@ -364,6 +389,9 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) smp_s1(tfm, conn->tk, conn->prnd, random, key); swap128(key, hcon->ltk); + + memset(hcon->ltk + conn->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); } return 0; -- cgit v1.1 From 330605423ca6eafafb8dcc27502bce1c585d1b06 Mon Sep 17 00:00:00 2001 From: Ilia Kolomisnky Date: Wed, 15 Jun 2011 06:52:26 +0300 Subject: Bluetooth: Fix L2CAP connection establishment In hci_conn_security ( which is used during L2CAP connection establishment ) test for HCI_CONN_ENCRYPT_PEND state also sets this state, which is bogus and leads to connection time-out on L2CAP sockets in certain situations (especially when using non-ssp devices ) Signed-off-by: Ilia Kolomisnky Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_conn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b9aa986..d3a05b9 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -608,7 +608,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) goto encrypt; auth: - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) return 0; if (!hci_conn_auth(conn, sec_level, auth_type)) -- cgit v1.1 From b2a66aad8620337e38d6692f03d94a03d5129840 Mon Sep 17 00:00:00 2001 From: Antti Julku Date: Wed, 15 Jun 2011 12:01:14 +0300 Subject: Bluetooth: Move blacklisting functions to hci_core Move blacklisting functions to hci_core.c, so that they can be used by both management interface and hci socket interface. Signed-off-by: Antti Julku Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_sock.c | 70 ++++-------------------------------------- 2 files changed, 85 insertions(+), 64 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f62ca19..8f5bee1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1205,6 +1205,85 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, return 0; } +struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, + bdaddr_t *bdaddr) +{ + struct list_head *p; + + list_for_each(p, &hdev->blacklist) { + struct bdaddr_list *b; + + b = list_entry(p, struct bdaddr_list, list); + + if (bacmp(bdaddr, &b->bdaddr) == 0) + return b; + } + + return NULL; +} + +int hci_blacklist_clear(struct hci_dev *hdev) +{ + struct list_head *p, *n; + + list_for_each_safe(p, n, &hdev->blacklist) { + struct bdaddr_list *b; + + b = list_entry(p, struct bdaddr_list, list); + + list_del(p); + kfree(b); + } + + return 0; +} + +int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct bdaddr_list *entry; + + hci_dev_lock(hdev); + + if (bacmp(bdaddr, BDADDR_ANY) == 0) + return -EBADF; + + if (hci_blacklist_lookup(hdev, bdaddr)) + return -EEXIST; + + entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + bacpy(&entry->bdaddr, bdaddr); + + list_add(&entry->list, &hdev->blacklist); + + hci_dev_unlock(hdev); + + return 0; +} + +int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct bdaddr_list *entry; + + hci_dev_lock(hdev); + + if (bacmp(bdaddr, BDADDR_ANY) == 0) + return hci_blacklist_clear(hdev); + + entry = hci_blacklist_lookup(hdev, bdaddr); + if (!entry) + return -ENOENT; + + list_del(&entry->list); + kfree(entry); + + hci_dev_unlock(hdev); + + return 0; +} + static void hci_clear_adv_cache(unsigned long arg) { struct hci_dev *hdev = (void *) arg; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 295e4a8..ff02cf5 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -180,82 +180,24 @@ static int hci_sock_release(struct socket *sock) return 0; } -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct list_head *p; - - list_for_each(p, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(p, struct bdaddr_list, list); - - if (bacmp(bdaddr, &b->bdaddr) == 0) - return b; - } - - return NULL; -} - -static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg) +static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; - struct bdaddr_list *entry; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - if (bacmp(&bdaddr, BDADDR_ANY) == 0) - return -EBADF; - - if (hci_blacklist_lookup(hdev, &bdaddr)) - return -EEXIST; - - entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - bacpy(&entry->bdaddr, &bdaddr); - - list_add(&entry->list, &hdev->blacklist); - - return 0; -} - -int hci_blacklist_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(p, struct bdaddr_list, list); - - list_del(p); - kfree(b); - } - - return 0; + return hci_blacklist_add(hdev, &bdaddr); } -static int hci_blacklist_del(struct hci_dev *hdev, void __user *arg) +static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; - struct bdaddr_list *entry; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - if (bacmp(&bdaddr, BDADDR_ANY) == 0) - return hci_blacklist_clear(hdev); - - entry = hci_blacklist_lookup(hdev, &bdaddr); - if (!entry) - return -ENOENT; - - list_del(&entry->list); - kfree(entry); - - return 0; + return hci_blacklist_del(hdev, &bdaddr); } /* Ioctls that require bound socket */ @@ -290,12 +232,12 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign case HCIBLOCKADDR: if (!capable(CAP_NET_ADMIN)) return -EACCES; - return hci_blacklist_add(hdev, (void __user *) arg); + return hci_sock_blacklist_add(hdev, (void __user *) arg); case HCIUNBLOCKADDR: if (!capable(CAP_NET_ADMIN)) return -EACCES; - return hci_blacklist_del(hdev, (void __user *) arg); + return hci_sock_blacklist_del(hdev, (void __user *) arg); default: if (hdev->ioctl) -- cgit v1.1 From 7fbec224cfb44074ab88720c878aa3bdb3158377 Mon Sep 17 00:00:00 2001 From: Antti Julku Date: Wed, 15 Jun 2011 12:01:15 +0300 Subject: Bluetooth: Add blacklisting support for mgmt interface Management interface commands for blocking and unblocking devices. Signed-off-by: Antti Julku Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'net') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d192089..64c0418 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1666,6 +1666,70 @@ failed: return err; } +static int block_device(struct sock *sk, u16 index, unsigned char *data, + u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_block_device *cp; + int err; + + BT_DBG("hci%u", index); + + cp = (void *) data; + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, + EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, + ENODEV); + + err = hci_blacklist_add(hdev, &cp->bdaddr); + + if (err < 0) + err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err); + else + err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, + NULL, 0); + hci_dev_put(hdev); + + return err; +} + +static int unblock_device(struct sock *sk, u16 index, unsigned char *data, + u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_unblock_device *cp; + int err; + + BT_DBG("hci%u", index); + + cp = (void *) data; + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, + EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, + ENODEV); + + err = hci_blacklist_del(hdev, &cp->bdaddr); + + if (err < 0) + err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err); + else + err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, + NULL, 0); + hci_dev_put(hdev); + + return err; +} + int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; @@ -1780,6 +1844,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_STOP_DISCOVERY: err = stop_discovery(sk, index); break; + case MGMT_OP_BLOCK_DEVICE: + err = block_device(sk, index, buf + sizeof(*hdr), len); + break; + case MGMT_OP_UNBLOCK_DEVICE: + err = unblock_device(sk, index, buf + sizeof(*hdr), len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, 0x01); -- cgit v1.1 From c1360a1cf35117d6f3898cb5183ce4349d06714c Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 10 Jun 2011 17:02:12 -0300 Subject: Bluetooth: use bit operation on conf_state Instead of making the bit operations manually, we now use set_bit, test_bit, etc. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 77 +++++++++++++++++++++------------------------- net/bluetooth/l2cap_sock.c | 4 +-- 2 files changed, 37 insertions(+), 44 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bbbae2e..b4e927e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -381,8 +381,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) } else sk->sk_state_change(sk); - if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE && - chan->conf_state & L2CAP_CONF_INPUT_DONE)) + if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) && + test_bit(CONF_INPUT_DONE, &chan->conf_state))) return; skb_queue_purge(&chan->tx_q); @@ -633,7 +633,7 @@ static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control) static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) { - return !(chan->conf_state & L2CAP_CONF_CONNECT_PEND); + return !test_bit(CONF_CONNECT_PEND, &chan->conf_state); } static void l2cap_do_start(struct l2cap_chan *chan) @@ -651,7 +651,7 @@ static void l2cap_do_start(struct l2cap_chan *chan) req.psm = chan->psm; chan->ident = l2cap_get_ident(conn); - chan->conf_state |= L2CAP_CONF_CONNECT_PEND; + set_bit(CONF_CONNECT_PEND, &chan->conf_state); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); @@ -740,10 +740,9 @@ static void l2cap_conn_start(struct l2cap_conn *conn) continue; } - if (!l2cap_mode_supported(chan->mode, - conn->feat_mask) - && chan->conf_state & - L2CAP_CONF_STATE2_DEVICE) { + if (!l2cap_mode_supported(chan->mode, conn->feat_mask) + && test_bit(CONF_STATE2_DEVICE, + &chan->conf_state)) { /* l2cap_chan_close() calls list_del(chan) * so release the lock */ read_unlock_bh(&conn->chan_lock); @@ -757,7 +756,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) req.psm = chan->psm; chan->ident = l2cap_get_ident(conn); - chan->conf_state |= L2CAP_CONF_CONNECT_PEND; + set_bit(CONF_CONNECT_PEND, &chan->conf_state); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); @@ -788,13 +787,13 @@ static void l2cap_conn_start(struct l2cap_conn *conn) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); - if (chan->conf_state & L2CAP_CONF_REQ_SENT || + if (test_bit(CONF_REQ_SENT, &chan->conf_state) || rsp.result != L2CAP_CR_SUCCESS) { bh_unlock_sock(sk); continue; } - chan->conf_state |= L2CAP_CONF_REQ_SENT; + set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; @@ -1917,7 +1916,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) switch (chan->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: - if (chan->conf_state & L2CAP_CONF_STATE2_DEVICE) + if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) break; /* fall through */ @@ -1964,7 +1963,7 @@ done: break; if (chan->fcs == L2CAP_FCS_NONE || - chan->conf_state & L2CAP_CONF_NO_FCS_RECV) { + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } @@ -1987,7 +1986,7 @@ done: break; if (chan->fcs == L2CAP_FCS_NONE || - chan->conf_state & L2CAP_CONF_NO_FCS_RECV) { + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } @@ -2039,7 +2038,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) case L2CAP_CONF_FCS: if (val == L2CAP_FCS_NONE) - chan->conf_state |= L2CAP_CONF_NO_FCS_RECV; + set_bit(CONF_NO_FCS_RECV, &chan->conf_state); break; @@ -2059,7 +2058,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) switch (chan->mode) { case L2CAP_MODE_STREAMING: case L2CAP_MODE_ERTM: - if (!(chan->conf_state & L2CAP_CONF_STATE2_DEVICE)) { + if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask); break; @@ -2092,14 +2091,14 @@ done: result = L2CAP_CONF_UNACCEPT; else { chan->omtu = mtu; - chan->conf_state |= L2CAP_CONF_MTU_DONE; + set_bit(CONF_MTU_DONE, &chan->conf_state); } l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu); switch (rfc.mode) { case L2CAP_MODE_BASIC: chan->fcs = L2CAP_FCS_NONE; - chan->conf_state |= L2CAP_CONF_MODE_DONE; + set_bit(CONF_MODE_DONE, &chan->conf_state); break; case L2CAP_MODE_ERTM: @@ -2116,7 +2115,7 @@ done: rfc.monitor_timeout = le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO); - chan->conf_state |= L2CAP_CONF_MODE_DONE; + set_bit(CONF_MODE_DONE, &chan->conf_state); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2129,7 +2128,7 @@ done: chan->remote_mps = le16_to_cpu(rfc.max_pdu_size); - chan->conf_state |= L2CAP_CONF_MODE_DONE; + set_bit(CONF_MODE_DONE, &chan->conf_state); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2144,7 +2143,7 @@ done: } if (result == L2CAP_CONF_SUCCESS) - chan->conf_state |= L2CAP_CONF_OUTPUT_DONE; + set_bit(CONF_OUTPUT_DONE, &chan->conf_state); } rsp->scid = cpu_to_le16(chan->dcid); rsp->result = cpu_to_le16(result); @@ -2186,7 +2185,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi if (olen == sizeof(rfc)) memcpy(&rfc, (void *)val, olen); - if ((chan->conf_state & L2CAP_CONF_STATE2_DEVICE) && + if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && rfc.mode != chan->mode) return -ECONNREFUSED; @@ -2248,10 +2247,9 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); - if (chan->conf_state & L2CAP_CONF_REQ_SENT) + if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) return; - chan->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; @@ -2433,10 +2431,10 @@ sendresp: L2CAP_INFO_REQ, sizeof(info), &info); } - if (chan && !(chan->conf_state & L2CAP_CONF_REQ_SENT) && + if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) && result == L2CAP_CR_SUCCESS) { u8 buf[128]; - chan->conf_state |= L2CAP_CONF_REQ_SENT; + set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; @@ -2477,20 +2475,18 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd l2cap_state_change(chan, BT_CONFIG); chan->ident = 0; chan->dcid = dcid; - chan->conf_state &= ~L2CAP_CONF_CONNECT_PEND; + clear_bit(CONF_CONNECT_PEND, &chan->conf_state); - if (chan->conf_state & L2CAP_CONF_REQ_SENT) + if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) break; - chan->conf_state |= L2CAP_CONF_REQ_SENT; - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, req), req); chan->num_conf_req++; break; case L2CAP_CR_PEND: - chan->conf_state |= L2CAP_CONF_CONNECT_PEND; + set_bit(CONF_CONNECT_PEND, &chan->conf_state); break; default: @@ -2512,14 +2508,12 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd static inline void set_default_fcs(struct l2cap_chan *chan) { - struct l2cap_pinfo *pi = l2cap_pi(chan->sk); - /* FCS is enabled only in ERTM or streaming mode, if one or both * sides request it. */ if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING) chan->fcs = L2CAP_FCS_NONE; - else if (!(pi->chan->conf_state & L2CAP_CONF_NO_FCS_RECV)) + else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) chan->fcs = L2CAP_FCS_CRC16; } @@ -2586,10 +2580,10 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* Reset config buffer. */ chan->conf_len = 0; - if (!(chan->conf_state & L2CAP_CONF_OUTPUT_DONE)) + if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) goto unlock; - if (chan->conf_state & L2CAP_CONF_INPUT_DONE) { + if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { set_default_fcs(chan); l2cap_state_change(chan, BT_CONNECTED); @@ -2604,9 +2598,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr goto unlock; } - if (!(chan->conf_state & L2CAP_CONF_REQ_SENT)) { + if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) { u8 buf[64]; - chan->conf_state |= L2CAP_CONF_REQ_SENT; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; @@ -2679,9 +2672,9 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (flags & 0x01) goto done; - chan->conf_state |= L2CAP_CONF_INPUT_DONE; + set_bit(CONF_INPUT_DONE, &chan->conf_state); - if (chan->conf_state & L2CAP_CONF_OUTPUT_DONE) { + if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) { set_default_fcs(chan); l2cap_state_change(chan, BT_CONNECTED); @@ -4203,7 +4196,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) continue; } - if (chan->conf_state & L2CAP_CONF_CONNECT_PEND) { + if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { bh_unlock_sock(sk); continue; } @@ -4222,7 +4215,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) req.psm = chan->psm; chan->ident = l2cap_get_ident(conn); - chan->conf_state |= L2CAP_CONF_CONNECT_PEND; + set_bit(CONF_CONNECT_PEND, &chan->conf_state); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 5c819e0..39082d4 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -512,7 +512,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us chan->mode = opts.mode; switch (chan->mode) { case L2CAP_MODE_BASIC: - chan->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; + clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -891,7 +891,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->omtu = 0; if (!disable_ertm && sk->sk_type == SOCK_STREAM) { chan->mode = L2CAP_MODE_ERTM; - chan->conf_state |= L2CAP_CONF_STATE2_DEVICE; + set_bit(CONF_STATE2_DEVICE, &chan->conf_state); } else { chan->mode = L2CAP_MODE_BASIC; } -- cgit v1.1 From e2ab43536c53ba112a0adfb4c0dba286544c41f6 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 10 Jun 2011 21:28:49 -0300 Subject: Bluetooth: Use bit operations on conn_state Instead of setting bits manually we use set_bit, test_bit, etc. Also remove L2CAP_ prefix from macros. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 161 +++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 87 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b4e927e..25f31f4 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -584,15 +584,11 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) count = min_t(unsigned int, conn->mtu, hlen); control |= L2CAP_CTRL_FRAME_TYPE; - if (chan->conn_state & L2CAP_CONN_SEND_FBIT) { + if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; - chan->conn_state &= ~L2CAP_CONN_SEND_FBIT; - } - if (chan->conn_state & L2CAP_CONN_SEND_PBIT) { + if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state)) control |= L2CAP_CTRL_POLL; - chan->conn_state &= ~L2CAP_CONN_SEND_PBIT; - } skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) @@ -620,9 +616,9 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control) { - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control |= L2CAP_SUPER_RCV_NOT_READY; - chan->conn_state |= L2CAP_CONN_RNR_SENT; + set_bit(CONN_RNR_SENT, &chan->conn_state); } else control |= L2CAP_SUPER_RCV_READY; @@ -1223,7 +1219,7 @@ static void l2cap_retrans_timeout(unsigned long arg) chan->retry_count = 1; __set_monitor_timer(chan); - chan->conn_state |= L2CAP_CONN_WAIT_F; + set_bit(CONN_WAIT_F, &chan->conn_state); l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); bh_unlock_sock(sk); @@ -1314,10 +1310,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); control &= L2CAP_CTRL_SAR; - if (chan->conn_state & L2CAP_CONN_SEND_FBIT) { + if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; - chan->conn_state &= ~L2CAP_CONN_SEND_FBIT; - } control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); @@ -1356,10 +1350,9 @@ int l2cap_ertm_send(struct l2cap_chan *chan) control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); control &= L2CAP_CTRL_SAR; - if (chan->conn_state & L2CAP_CONN_SEND_FBIT) { + if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; - chan->conn_state &= ~L2CAP_CONN_SEND_FBIT; - } + control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); @@ -1411,9 +1404,9 @@ static void l2cap_send_ack(struct l2cap_chan *chan) control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control |= L2CAP_SUPER_RCV_NOT_READY; - chan->conn_state |= L2CAP_CONN_RNR_SENT; + set_bit(CONN_RNR_SENT, &chan->conn_state); l2cap_send_sframe(chan, control); return; } @@ -1680,8 +1673,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) break; } - if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (chan->conn_state & L2CAP_CONN_WAIT_F)) { + if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && + test_bit(CONN_WAIT_F, &chan->conn_state)) { err = len; break; } @@ -3071,18 +3064,18 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control |= L2CAP_SUPER_RCV_NOT_READY; l2cap_send_sframe(chan, control); - chan->conn_state |= L2CAP_CONN_RNR_SENT; + set_bit(CONN_RNR_SENT, &chan->conn_state); } - if (chan->conn_state & L2CAP_CONN_REMOTE_BUSY) + if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) l2cap_retransmit_frames(chan); l2cap_ertm_send(chan); - if (!(chan->conn_state & L2CAP_CONN_LOCAL_BUSY) && + if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) && chan->frames_sent == 0) { control |= L2CAP_SUPER_RCV_READY; l2cap_send_sframe(chan, control); @@ -3138,13 +3131,13 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: - if (chan->conn_state & L2CAP_CONN_SAR_SDU) + if (test_bit(CONN_SAR_SDU, &chan->conn_state)) goto drop; return chan->ops->recv(chan->data, skb); case L2CAP_SDU_START: - if (chan->conn_state & L2CAP_CONN_SAR_SDU) + if (test_bit(CONN_SAR_SDU, &chan->conn_state)) goto drop; chan->sdu_len = get_unaligned_le16(skb->data); @@ -3163,12 +3156,12 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - chan->conn_state |= L2CAP_CONN_SAR_SDU; + set_bit(CONN_SAR_SDU, &chan->conn_state); chan->partial_sdu_len = skb->len; break; case L2CAP_SDU_CONTINUE: - if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) + if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) goto disconnect; if (!chan->sdu) @@ -3183,13 +3176,13 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk break; case L2CAP_SDU_END: - if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) + if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) goto disconnect; if (!chan->sdu) goto disconnect; - if (!(chan->conn_state & L2CAP_CONN_SAR_RETRY)) { + if (!test_bit(CONN_SAR_RETRY, &chan->conn_state)) { chan->partial_sdu_len += skb->len; if (chan->partial_sdu_len > chan->imtu) @@ -3203,19 +3196,19 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk _skb = skb_clone(chan->sdu, GFP_ATOMIC); if (!_skb) { - chan->conn_state |= L2CAP_CONN_SAR_RETRY; + set_bit(CONN_SAR_RETRY, &chan->conn_state); return -ENOMEM; } err = chan->ops->recv(chan->data, _skb); if (err < 0) { kfree_skb(_skb); - chan->conn_state |= L2CAP_CONN_SAR_RETRY; + set_bit(CONN_SAR_RETRY, &chan->conn_state); return err; } - chan->conn_state &= ~L2CAP_CONN_SAR_RETRY; - chan->conn_state &= ~L2CAP_CONN_SAR_SDU; + clear_bit(CONN_SAR_RETRY, &chan->conn_state); + clear_bit(CONN_SAR_SDU, &chan->conn_state); kfree_skb(chan->sdu); break; @@ -3251,7 +3244,7 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) chan->buffer_seq = (chan->buffer_seq + 1) % 64; } - if (!(chan->conn_state & L2CAP_CONN_RNR_SENT)) + if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) goto done; control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; @@ -3262,11 +3255,11 @@ static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) __clear_retrans_timer(chan); __set_monitor_timer(chan); - chan->conn_state |= L2CAP_CONN_WAIT_F; + set_bit(CONN_WAIT_F, &chan->conn_state); done: - chan->conn_state &= ~L2CAP_CONN_LOCAL_BUSY; - chan->conn_state &= ~L2CAP_CONN_RNR_SENT; + clear_bit(CONN_LOCAL_BUSY, &chan->conn_state); + clear_bit(CONN_RNR_SENT, &chan->conn_state); BT_DBG("chan %p, Exit local busy", chan); @@ -3324,7 +3317,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c { int sctrl, err; - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) { + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; __skb_queue_tail(&chan->busy_q, skb); return l2cap_try_push_rx_skb(chan); @@ -3341,7 +3334,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c /* Busy Condition */ BT_DBG("chan %p, Enter local busy", chan); - chan->conn_state |= L2CAP_CONN_LOCAL_BUSY; + set_bit(CONN_LOCAL_BUSY, &chan->conn_state); bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; __skb_queue_tail(&chan->busy_q, skb); @@ -3349,7 +3342,7 @@ static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 c sctrl |= L2CAP_SUPER_RCV_NOT_READY; l2cap_send_sframe(chan, sctrl); - chan->conn_state |= L2CAP_CONN_RNR_SENT; + set_bit(CONN_RNR_SENT, &chan->conn_state); __clear_ack_timer(chan); @@ -3370,7 +3363,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: - if (chan->conn_state & L2CAP_CONN_SAR_SDU) { + if (test_bit(CONN_SAR_SDU, &chan->conn_state)) { kfree_skb(chan->sdu); break; } @@ -3382,7 +3375,7 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf break; case L2CAP_SDU_START: - if (chan->conn_state & L2CAP_CONN_SAR_SDU) { + if (test_bit(CONN_SAR_SDU, &chan->conn_state)) { kfree_skb(chan->sdu); break; } @@ -3403,13 +3396,13 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - chan->conn_state |= L2CAP_CONN_SAR_SDU; + set_bit(CONN_SAR_SDU, &chan->conn_state); chan->partial_sdu_len = skb->len; err = 0; break; case L2CAP_SDU_CONTINUE: - if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) + if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) break; memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); @@ -3423,12 +3416,12 @@ static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buf break; case L2CAP_SDU_END: - if (!(chan->conn_state & L2CAP_CONN_SAR_SDU)) + if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) break; memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - chan->conn_state &= ~L2CAP_CONN_SAR_SDU; + clear_bit(CONN_SAR_SDU, &chan->conn_state); chan->partial_sdu_len += skb->len; if (chan->partial_sdu_len > chan->imtu) @@ -3519,11 +3512,11 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont tx_seq, rx_control); if (L2CAP_CTRL_FINAL & rx_control && - chan->conn_state & L2CAP_CONN_WAIT_F) { + test_bit(CONN_WAIT_F, &chan->conn_state)) { __clear_monitor_timer(chan); if (chan->unacked_frames > 0) __set_retrans_timer(chan); - chan->conn_state &= ~L2CAP_CONN_WAIT_F; + clear_bit(CONN_WAIT_F, &chan->conn_state); } chan->expected_ack_seq = req_seq; @@ -3542,10 +3535,10 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont goto drop; } - if (chan->conn_state & L2CAP_CONN_LOCAL_BUSY) + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) goto drop; - if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { struct srej_list *first; first = list_first_entry(&chan->srej_l, @@ -3559,7 +3552,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont if (list_empty(&chan->srej_l)) { chan->buffer_seq = chan->buffer_seq_srej; - chan->conn_state &= ~L2CAP_CONN_SREJ_SENT; + clear_bit(CONN_SREJ_SENT, &chan->conn_state); l2cap_send_ack(chan); BT_DBG("chan %p, Exit SREJ_SENT", chan); } @@ -3588,7 +3581,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont if (tx_seq_offset < expected_tx_seq_offset) goto drop; - chan->conn_state |= L2CAP_CONN_SREJ_SENT; + set_bit(CONN_SREJ_SENT, &chan->conn_state); BT_DBG("chan %p, Enter SREJ", chan); @@ -3599,7 +3592,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont __skb_queue_head_init(&chan->busy_q); l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); - chan->conn_state |= L2CAP_CONN_SEND_PBIT; + set_bit(CONN_SEND_PBIT, &chan->conn_state); l2cap_send_srejframe(chan, tx_seq); @@ -3610,7 +3603,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont expected: chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; - if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { bt_cb(skb)->tx_seq = tx_seq; bt_cb(skb)->sar = sar; __skb_queue_tail(&chan->srej_q, skb); @@ -3622,9 +3615,7 @@ expected: return 0; if (rx_control & L2CAP_CTRL_FINAL) { - if (chan->conn_state & L2CAP_CONN_REJ_ACT) - chan->conn_state &= ~L2CAP_CONN_REJ_ACT; - else + if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } @@ -3650,33 +3641,31 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_POLL) { - chan->conn_state |= L2CAP_CONN_SEND_FBIT; - if (chan->conn_state & L2CAP_CONN_SREJ_SENT) { - if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && + set_bit(CONN_SEND_FBIT, &chan->conn_state); + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { + if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && (chan->unacked_frames > 0)) __set_retrans_timer(chan); - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); l2cap_send_srejtail(chan); } else { l2cap_send_i_or_rr_or_rnr(chan); } } else if (rx_control & L2CAP_CTRL_FINAL) { - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - if (chan->conn_state & L2CAP_CONN_REJ_ACT) - chan->conn_state &= ~L2CAP_CONN_REJ_ACT; - else + if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } else { - if ((chan->conn_state & L2CAP_CONN_REMOTE_BUSY) && + if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && (chan->unacked_frames > 0)) __set_retrans_timer(chan); - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; - if (chan->conn_state & L2CAP_CONN_SREJ_SENT) + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) l2cap_send_ack(chan); else l2cap_ertm_send(chan); @@ -3689,21 +3678,19 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_FINAL) { - if (chan->conn_state & L2CAP_CONN_REJ_ACT) - chan->conn_state &= ~L2CAP_CONN_REJ_ACT; - else + if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } else { l2cap_retransmit_frames(chan); - if (chan->conn_state & L2CAP_CONN_WAIT_F) - chan->conn_state |= L2CAP_CONN_REJ_ACT; + if (test_bit(CONN_WAIT_F, &chan->conn_state)) + set_bit(CONN_REJ_ACT, &chan->conn_state); } } static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control) @@ -3712,32 +3699,32 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_ BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); - chan->conn_state &= ~L2CAP_CONN_REMOTE_BUSY; + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); if (rx_control & L2CAP_CTRL_POLL) { chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); - chan->conn_state |= L2CAP_CONN_SEND_FBIT; + set_bit(CONN_SEND_FBIT, &chan->conn_state); l2cap_retransmit_one_frame(chan, tx_seq); l2cap_ertm_send(chan); - if (chan->conn_state & L2CAP_CONN_WAIT_F) { + if (test_bit(CONN_WAIT_F, &chan->conn_state)) { chan->srej_save_reqseq = tx_seq; - chan->conn_state |= L2CAP_CONN_SREJ_ACT; + set_bit(CONN_SREJ_ACT, &chan->conn_state); } } else if (rx_control & L2CAP_CTRL_FINAL) { - if ((chan->conn_state & L2CAP_CONN_SREJ_ACT) && + if (test_bit(CONN_SREJ_ACT, &chan->conn_state) && chan->srej_save_reqseq == tx_seq) - chan->conn_state &= ~L2CAP_CONN_SREJ_ACT; + clear_bit(CONN_SREJ_ACT, &chan->conn_state); else l2cap_retransmit_one_frame(chan, tx_seq); } else { l2cap_retransmit_one_frame(chan, tx_seq); - if (chan->conn_state & L2CAP_CONN_WAIT_F) { + if (test_bit(CONN_WAIT_F, &chan->conn_state)) { chan->srej_save_reqseq = tx_seq; - chan->conn_state |= L2CAP_CONN_SREJ_ACT; + set_bit(CONN_SREJ_ACT, &chan->conn_state); } } } @@ -3748,14 +3735,14 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); - chan->conn_state |= L2CAP_CONN_REMOTE_BUSY; + set_bit(CONN_REMOTE_BUSY, &chan->conn_state); chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_POLL) - chan->conn_state |= L2CAP_CONN_SEND_FBIT; + set_bit(CONN_SEND_FBIT, &chan->conn_state); - if (!(chan->conn_state & L2CAP_CONN_SREJ_SENT)) { + if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) { __clear_retrans_timer(chan); if (rx_control & L2CAP_CTRL_POLL) l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); @@ -3773,11 +3760,11 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len); if (L2CAP_CTRL_FINAL & rx_control && - chan->conn_state & L2CAP_CONN_WAIT_F) { + test_bit(CONN_WAIT_F, &chan->conn_state)) { __clear_monitor_timer(chan); if (chan->unacked_frames > 0) __set_retrans_timer(chan); - chan->conn_state &= ~L2CAP_CONN_WAIT_F; + clear_bit(CONN_WAIT_F, &chan->conn_state); } switch (rx_control & L2CAP_CTRL_SUPERVISE) { -- cgit v1.1 From a6af1d848179c17deb94621c2e761769f0d99355 Mon Sep 17 00:00:00 2001 From: Paul Stewart Date: Fri, 10 Jun 2011 07:00:19 -0800 Subject: mac80211: Start monitor work on restart Trigger connection monitor on resume from suspend. Since we have been sleeping, there is reason to suspect that we might not still be associated. The speed of detecting loss of {connection,authentication} is worth the cost of the small additional traffic at resume. Signed-off-by: Paul Stewart Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0c6e9ef..faca503 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2206,6 +2206,7 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) add_timer(&ifmgd->chswitch_timer); ieee80211_sta_reset_beacon_monitor(sdata); ieee80211_restart_sta_timer(sdata); + ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.monitor_work); } #endif -- cgit v1.1 From b856439b1b54358e580aaee5dbe683af5ada9403 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 13 Jun 2011 12:47:30 +0300 Subject: mac80211: add cancel_hw_scan() callback When suspending, __ieee80211_suspend() calls ieee80211_scan_cancel(), which will only cancel sw scan. In order to cancel hw scan, the low-level driver has to cancel it in the suspend() callback. however, this is too late, as a new scan_work will be enqueued (while the driver is going into suspend). Add a new cancel_hw_scan() callback, asking the driver to cancel an active hw scan, and call it in ieee80211_scan_cancel(). Signed-off-by: Eliad Peller Reviewed-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- net/mac80211/driver-ops.h | 10 ++++++++++ net/mac80211/driver-trace.h | 6 ++++++ net/mac80211/scan.c | 37 +++++++++++++++++++++---------------- 3 files changed, 37 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index eebf7a6..0e7e426 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -218,6 +218,16 @@ static inline int drv_hw_scan(struct ieee80211_local *local, return ret; } +static inline void drv_cancel_hw_scan(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + + trace_drv_cancel_hw_scan(local, sdata); + local->ops->cancel_hw_scan(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + static inline int drv_sched_scan_start(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index ed9edcb..3cb6795 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -460,6 +460,12 @@ DEFINE_EVENT(local_sdata_evt, drv_hw_scan, TP_ARGS(local, sdata) ); +DEFINE_EVENT(local_sdata_evt, drv_cancel_hw_scan, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + DEFINE_EVENT(local_sdata_evt, drv_sched_scan_start, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata), diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 58ffa7d..1758b46 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -821,10 +821,8 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, */ void ieee80211_scan_cancel(struct ieee80211_local *local) { - bool abortscan; - /* - * We are only canceling software scan, or deferred scan that was not + * We are canceling software scan, or deferred scan that was not * yet really started (see __ieee80211_start_scan ). * * Regarding hardware scan: @@ -836,23 +834,30 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) * - we can not cancel scan_work since driver can schedule it * by ieee80211_scan_completed(..., true) to finish scan * - * Hence low lever driver is responsible for canceling HW scan. + * Hence we only call the cancel_hw_scan() callback, but the low-level + * driver is still responsible for calling ieee80211_scan_completed() + * after the scan was completed/aborted. */ mutex_lock(&local->mtx); - abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning); - if (abortscan) { - /* - * The scan is canceled, but stop work from being pending. - * - * If the work is currently running, it must be blocked on - * the mutex, but we'll set scan_sdata = NULL and it'll - * simply exit once it acquires the mutex. - */ - cancel_delayed_work(&local->scan_work); - /* and clean up */ - __ieee80211_scan_completed(&local->hw, true, false); + if (!local->scan_req) + goto out; + + if (test_bit(SCAN_HW_SCANNING, &local->scanning)) { + if (local->ops->cancel_hw_scan) + drv_cancel_hw_scan(local, local->scan_sdata); + goto out; } + + /* + * If the work is currently running, it must be blocked on + * the mutex, but we'll set scan_sdata = NULL and it'll + * simply exit once it acquires the mutex. + */ + cancel_delayed_work(&local->scan_work); + /* and clean up */ + __ieee80211_scan_completed(&local->hw, true, false); +out: mutex_unlock(&local->mtx); } -- cgit v1.1 From a7925bd27d6ef020b8f3a3a64bde61d0add07220 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 17 Jun 2011 16:15:10 -0300 Subject: Bluetooth: Fix locking in blacklist code There was no unlock call on the errors path Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8f5bee1..0029e17 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1241,47 +1241,59 @@ int hci_blacklist_clear(struct hci_dev *hdev) int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *entry; - - hci_dev_lock(hdev); + int err; if (bacmp(bdaddr, BDADDR_ANY) == 0) return -EBADF; - if (hci_blacklist_lookup(hdev, bdaddr)) - return -EEXIST; + hci_dev_lock(hdev); + + if (hci_blacklist_lookup(hdev, bdaddr)) { + err = -EEXIST; + goto err; + } entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) + if (!entry) { return -ENOMEM; + goto err; + } bacpy(&entry->bdaddr, bdaddr); list_add(&entry->list, &hdev->blacklist); - hci_dev_unlock(hdev); + err = 0; - return 0; +err: + hci_dev_unlock(hdev); + return err; } int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *entry; + int err = 0; hci_dev_lock(hdev); - if (bacmp(bdaddr, BDADDR_ANY) == 0) - return hci_blacklist_clear(hdev); + if (bacmp(bdaddr, BDADDR_ANY) == 0) { + hci_blacklist_clear(hdev); + goto done; + } entry = hci_blacklist_lookup(hdev, bdaddr); - if (!entry) - return -ENOENT; + if (!entry) { + err = -ENOENT; + goto done; + } list_del(&entry->list); kfree(entry); +done: hci_dev_unlock(hdev); - - return 0; + return err; } static void hci_clear_adv_cache(unsigned long arg) -- cgit v1.1 From 6312845169e8719f6f0726efc62bc6a8dedea9d3 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Fri, 17 Jun 2011 22:46:26 -0300 Subject: Bluetooth: Fix crash when setting a LE socket to ready We should not try to do any other type of configuration for LE links when they become ready. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 25f31f4..b8c1df2 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -918,11 +918,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) bh_lock_sock(sk); - if (conn->hcon->type == LE_LINK) + if (conn->hcon->type == LE_LINK) { if (smp_conn_security(conn, chan->sec_level)) l2cap_chan_ready(sk); - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { + } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { __clear_chan_timer(chan); l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); -- cgit v1.1 From e13e21dc5d06bd9ed4a88daf057b8dbe80c220de Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Fri, 17 Jun 2011 22:46:27 -0300 Subject: Bluetooth: Remove useless access to the socket We already have access to the chan, we don't have to access the socket to get its imtu. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b8c1df2..700d01e3 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3965,7 +3965,7 @@ static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, str if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) goto drop; - if (l2cap_pi(sk)->chan->imtu < skb->len) + if (chan->imtu < skb->len) goto drop; if (!chan->ops->recv(chan->data, skb)) @@ -3998,7 +3998,7 @@ static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) goto drop; - if (l2cap_pi(sk)->chan->imtu < skb->len) + if (chan->imtu < skb->len) goto drop; if (!chan->ops->recv(chan->data, skb)) -- cgit v1.1 From 77572fd13d7f468216b85e68a006000726a59e89 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 16 Jun 2011 11:49:33 +0300 Subject: mac80211: quiesce vif before suspending Cancel all relevant timers/works before suspending (wowlan). This patch handles the following warning: WARNING: at net/mac80211/util.c:565 queueing ieee80211 work while going to suspend Backtrace: [] (ieee80211_can_queue_work+0x0/0x4c [mac80211]) [] (ieee80211_queue_work+0x0/0x30 [mac80211]) [] (ieee80211_sta_timer+0x0/0x3c [mac80211]) [] (run_timer_softirq+0x0/0x220) [] (__do_softirq+0x0/0x130) [] (irq_exit+0x0/0xb4) [] (ipi_timer+0x0/0x4c) [] (do_local_timer+0x0/0x88) [] (cpu_idle+0x0/0xe0) [] (rest_init+0x0/0xe0) [] (start_kernel+0x0/0x314) Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- net/mac80211/pm.c | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 730778a..67839eb 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -6,6 +6,28 @@ #include "driver-ops.h" #include "led.h" +/* return value indicates whether the driver should be further notified */ +static bool ieee80211_quiesce(struct ieee80211_sub_if_data *sdata) +{ + switch (sdata->vif.type) { + case NL80211_IFTYPE_STATION: + ieee80211_sta_quiesce(sdata); + return true; + case NL80211_IFTYPE_ADHOC: + ieee80211_ibss_quiesce(sdata); + return true; + case NL80211_IFTYPE_MESH_POINT: + ieee80211_mesh_quiesce(sdata); + return true; + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MONITOR: + /* don't tell driver about this */ + return false; + default: + return true; + } +} + int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct ieee80211_local *local = hw_to_local(hw); @@ -54,6 +76,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) local->quiescing = false; return err; } + list_for_each_entry(sdata, &local->interfaces, list) { + cancel_work_sync(&sdata->work); + ieee80211_quiesce(sdata); + } goto suspend; } @@ -82,23 +108,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) list_for_each_entry(sdata, &local->interfaces, list) { cancel_work_sync(&sdata->work); - switch(sdata->vif.type) { - case NL80211_IFTYPE_STATION: - ieee80211_sta_quiesce(sdata); - break; - case NL80211_IFTYPE_ADHOC: - ieee80211_ibss_quiesce(sdata); - break; - case NL80211_IFTYPE_MESH_POINT: - ieee80211_mesh_quiesce(sdata); - break; - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_MONITOR: - /* don't tell driver about this */ + if (!ieee80211_quiesce(sdata)) continue; - default: - break; - } if (!ieee80211_sdata_running(sdata)) continue; -- cgit v1.1 From 43f3dc41571c47a1fbded9aca1cf0a737967b005 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Mon, 20 Jun 2011 18:53:18 -0300 Subject: Bluetooth: Fix not setting the chan state When the connection is ready we should set the connection to CONNECTED so userspace can use it. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ba0b2f4..9ec9c8c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -895,7 +895,7 @@ static void l2cap_chan_ready(struct sock *sk) chan->conf_state = 0; __clear_chan_timer(chan); - sk->sk_state = BT_CONNECTED; + l2cap_state_change(chan, BT_CONNECTED); sk->sk_state_change(sk); if (parent) -- cgit v1.1 From 670dc2833d144375eac36ad74111495a825a9288 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 20 Jun 2011 13:40:46 +0200 Subject: netlink: advertise incomplete dumps Consider the following situation: * a dump that would show 8 entries, four in the first round, and four in the second * between the first and second rounds, 6 entries are removed * now the second round will not show any entry, and even if there is a sequence/generation counter the application will not know To solve this problem, add a new flag NLM_F_DUMP_INTR to the netlink header that indicates the dump wasn't consistent, this flag can also be set on the MSG_DONE message that terminates the dump, and as such above situation can be detected. To achieve this, add a sequence counter to the netlink callback struct. Of course, netlink code still needs to use this new functionality. The correct way to do that is to always set cb->seq when a dumpit callback is invoked and call nl_dump_check_consistent() for each new message. The core code will also call this function for the final MSG_DONE message. To make it usable with generic netlink, a new function genlmsg_nlhdr() is needed to obtain the netlink header from the genetlink user header. Signed-off-by: Johannes Berg Acked-by: David S. Miller Signed-off-by: John W. Linville --- net/netlink/af_netlink.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6ef64ad..a7ec851 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1693,6 +1693,8 @@ static int netlink_dump(struct sock *sk) if (!nlh) goto errout_skb; + nl_dump_check_consistent(cb, nlh); + memcpy(nlmsg_data(nlh), &len, sizeof(len)); if (sk_filter(sk, skb)) -- cgit v1.1 From 9720bb3ab0b80659c63ed337eab66104a4156db0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 21 Jun 2011 09:45:33 +0200 Subject: nl80211: use netlink consistent dump feature for BSS dumps Use the new consistent dump feature from (generic) netlink to advertise when dumps are incomplete. Readers may note that this does not initialize the rdev->bss_generation counter to a non-zero value. This is still OK since the value is modified only under spinlock when the list is modified. Since the dump code holds the spinlock, the value will either be > 0 already, or the list will still be empty in which case a consistent dump will actually be made (and be empty). Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/nl80211.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 10823e2..aed6d2a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3620,7 +3620,8 @@ static int nl80211_stop_sched_scan(struct sk_buff *skb, return __cfg80211_stop_sched_scan(rdev, false); } -static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, +static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, + u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_internal_bss *intbss) @@ -3632,11 +3633,13 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, ASSERT_WDEV_LOCK(wdev); - hdr = nl80211hdr_put(msg, pid, seq, flags, + hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags, NL80211_CMD_NEW_SCAN_RESULTS); if (!hdr) return -1; + genl_dump_check_consistent(cb, hdr, &nl80211_fam); + NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex); @@ -3725,11 +3728,12 @@ static int nl80211_dump_scan(struct sk_buff *skb, spin_lock_bh(&rdev->bss_lock); cfg80211_bss_expire(rdev); + cb->seq = rdev->bss_generation; + list_for_each_entry(scan, &rdev->bss_list, list) { if (++idx <= start) continue; - if (nl80211_send_bss(skb, - NETLINK_CB(cb->skb).pid, + if (nl80211_send_bss(skb, cb, cb->nlh->nlmsg_seq, NLM_F_MULTI, rdev, wdev, scan) < 0) { idx--; -- cgit v1.1 From f70490e6078abe1182437e629f67a7f0b6f08cd4 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 23 Jun 2011 12:58:55 +1000 Subject: Bluetooth: include scatterlist.h where needed net/bluetooth/smp.c: In function 'smp_e': net/bluetooth/smp.c:49:21: error: storage size of 'sg' isn't known net/bluetooth/smp.c:67:2: error: implicit declaration of function 'sg_init_one' net/bluetooth/smp.c:49:21: warning: unused variable 'sg' Caused by commit d22ef0bc83c5 ("Bluetooth: Add LE SMP Cryptoolbox functions"). Missing include file, presumably. This batch has been in the bluetooth tree since June 14, so it may have been exposed by the removal of linux/mm.h from netdevice.h ... Signed-off-by: Stephen Rothwell Signed-off-by: John W. Linville --- net/bluetooth/smp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 52e9ec2..a36f870 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #define SMP_TIMEOUT 30000 /* 30 seconds */ -- cgit v1.1