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 --- include/net/bluetooth/l2cap.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 0529d278e068..d3a150955c44 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -354,6 +354,15 @@ struct l2cap_chan { struct list_head list; struct list_head global_l; + + void *data; + struct l2cap_ops *ops; +}; + +struct l2cap_ops { + char *name; + + struct l2cap_chan *(*new_connection) (void *data); }; struct l2cap_conn { @@ -460,9 +469,6 @@ int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); void l2cap_sock_kill(struct sock *sk); -void l2cap_sock_init(struct sock *sk, struct sock *parent); -struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio); struct l2cap_chan *l2cap_chan_create(struct sock *sk); void l2cap_chan_close(struct l2cap_chan *chan, int reason); -- cgit v1.2.3 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 --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 16 ++++++++-------- net/bluetooth/l2cap_sock.c | 8 ++++++++ 3 files changed, 17 insertions(+), 8 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d3a150955c44..05bb254c9ebd 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -363,6 +363,7 @@ struct l2cap_ops { char *name; struct l2cap_chan *(*new_connection) (void *data); + int (*recv) (void *data, struct sk_buff *skb); }; struct l2cap_conn { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8369f5680391..4cbb48d523e4 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 4050edeaf78b..28cdc7e6df54 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.2.3 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 --- include/net/bluetooth/l2cap.h | 3 +-- net/bluetooth/l2cap_core.c | 17 +++++++++-------- net/bluetooth/l2cap_sock.c | 10 +++++++++- 3 files changed, 19 insertions(+), 11 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 05bb254c9ebd..0ad61d069686 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -364,6 +364,7 @@ struct l2cap_ops { struct l2cap_chan *(*new_connection) (void *data); int (*recv) (void *data, struct sk_buff *skb); + void (*close) (void *data); }; struct l2cap_conn { @@ -469,8 +470,6 @@ int __l2cap_wait_ack(struct sock *sk); int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); -void l2cap_sock_kill(struct sock *sk); - struct l2cap_chan *l2cap_chan_create(struct sock *sk); void l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_destroy(struct l2cap_chan *chan); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4cbb48d523e4..3a121ac6b310 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 28cdc7e6df54..9f15a164993a 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.2.3 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 --- include/net/bluetooth/l2cap.h | 3 ++ net/bluetooth/l2cap_core.c | 113 ++++++++++++++++++++++-------------------- net/bluetooth/l2cap_sock.c | 12 +++++ 3 files changed, 74 insertions(+), 54 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 0ad61d069686..68c87244eafc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -287,6 +287,8 @@ struct l2cap_chan { struct l2cap_conn *conn; + __u8 state; + __le16 psm; __u16 dcid; __u16 scid; @@ -365,6 +367,7 @@ struct l2cap_ops { struct l2cap_chan *(*new_connection) (void *data); int (*recv) (void *data, struct sk_buff *skb); void (*close) (void *data); + void (*state_change) (void *data, int state); }; struct l2cap_conn { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3a121ac6b310..58fe03abee66 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 9f15a164993a..1d9c36509d7b 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.2.3 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 --- include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/l2cap_core.c | 30 +++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 68c87244eafc..b3d953b5f399 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -289,6 +289,8 @@ struct l2cap_chan { __u8 state; + atomic_t refcnt; + __le16 psm; __u16 dcid; __u16 scid; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 58fe03abee66..f24022c39869 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.2.3 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 --- include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/l2cap_core.c | 58 +++++++++++++++++++++---------------------- 2 files changed, 31 insertions(+), 29 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index b3d953b5f399..7aaf7f78ddb1 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -439,6 +439,8 @@ struct l2cap_pinfo { #define L2CAP_CONN_RNR_SENT 0x0200 #define L2CAP_CONN_SAR_RETRY 0x0400 +#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) +#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) #define __mod_retrans_timer() mod_timer(&chan->retrans_timer, \ jiffies + msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO)); #define __mod_monitor_timer() mod_timer(&chan->monitor_timer, \ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f24022c39869..5076976960eb 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.2.3 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 --- include/net/bluetooth/l2cap.h | 15 +++++++++------ net/bluetooth/l2cap_core.c | 44 +++++++++++++++++++++---------------------- 2 files changed, 31 insertions(+), 28 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7aaf7f78ddb1..c284be027d9f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -441,12 +441,15 @@ struct l2cap_pinfo { #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) -#define __mod_retrans_timer() mod_timer(&chan->retrans_timer, \ - jiffies + msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO)); -#define __mod_monitor_timer() mod_timer(&chan->monitor_timer, \ - jiffies + msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO)); -#define __mod_ack_timer() mod_timer(&chan->ack_timer, \ - jiffies + msecs_to_jiffies(L2CAP_DEFAULT_ACK_TO)); +#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ + L2CAP_DEFAULT_RETRANS_TO); +#define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer) +#define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \ + L2CAP_DEFAULT_MONITOR_TO); +#define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer) +#define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \ + L2CAP_DEFAULT_ACK_TO); +#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) static inline int l2cap_tx_window_full(struct l2cap_chan *ch) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 5076976960eb..3b31a1f1f020 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.2.3 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 --- include/net/bluetooth/smp.h | 26 ++++++++ net/bluetooth/Makefile | 2 +- net/bluetooth/smp.c | 146 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 net/bluetooth/smp.c (limited to 'include/net') diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 8f2edbf979dc..36bdd6eb6aa7 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -1,3 +1,25 @@ +/* + 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. +*/ + #ifndef __SMP_H #define __SMP_H @@ -73,4 +95,8 @@ struct smp_cmd_security_req { #define SMP_UNSPECIFIED 0x08 #define SMP_REPEATED_ATTEMPTS 0x09 +/* SMP Commands */ +int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); +int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); + #endif /* __SMP_H */ diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index f04fe9a9d634..9b67f3d08fa4 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 000000000000..aa0434f4aa1a --- /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.2.3 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 --- include/net/bluetooth/smp.h | 17 +++++++ net/bluetooth/smp.c | 107 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 119 insertions(+), 5 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 36bdd6eb6aa7..111853ab239a 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -38,6 +38,23 @@ struct smp_cmd_pairing { __u8 resp_key_dist; } __packed; +#define SMP_IO_DISPLAY_ONLY 0x00 +#define SMP_IO_DISPLAY_YESNO 0x01 +#define SMP_IO_KEYBOARD_ONLY 0x02 +#define SMP_IO_NO_INPUT_OUTPUT 0x03 +#define SMP_IO_KEYBOARD_DISPLAY 0x04 + +#define SMP_OOB_NOT_PRESENT 0x00 +#define SMP_OOB_PRESENT 0x01 + +#define SMP_DIST_ENC_KEY 0x01 +#define SMP_DIST_ID_KEY 0x02 +#define SMP_DIST_SIGN 0x04 + +#define SMP_AUTH_NONE 0x00 +#define SMP_AUTH_BONDING 0x01 +#define SMP_AUTH_MITM 0x04 + #define SMP_CMD_PAIRING_CONFIRM 0x03 struct smp_cmd_pairing_confirm { __u8 confirm_val[16]; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index aa0434f4aa1a..2240e96552f1 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.2.3 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 --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/Kconfig | 8 ++++++++ net/bluetooth/hci_core.c | 22 ++++++++++++++++++++++ net/bluetooth/smp.c | 22 ++++++++++++++++++---- 4 files changed, 50 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 836d3e8c4bf1..7837f3260863 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -177,6 +177,8 @@ struct hci_dev { __u16 init_last_cmd; + struct crypto_blkcipher *tfm; + struct inquiry_cache inq_cache; struct hci_conn_hash conn_hash; struct list_head blacklist; diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 6ae5ec508587..f495dea741e3 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 e14e8a1cb04e..f62ca1935f5a 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 2240e96552f1..aa20bee97501 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.2.3 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 --- include/net/bluetooth/l2cap.h | 5 +++++ net/bluetooth/smp.c | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index c284be027d9f..b03d9c4dfc78 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -395,6 +395,11 @@ struct l2cap_conn { __u8 disc_reason; + __u8 preq[7]; /* SMP Pairing Request */ + __u8 prsp[7]; /* SMP Pairing Response */ + __u8 prnd[16]; /* SMP Pairing Random */ + __u8 pcnf[16]; /* SMP Pairing Confirm */ + struct list_head chan_l; rwlock_t chan_lock; }; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 57fc7d0cadf3..fa22f4aa3b04 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.2.3 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 --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/smp.c | 97 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 14 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index b03d9c4dfc78..01c993b6b263 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -399,6 +399,7 @@ struct l2cap_conn { __u8 prsp[7]; /* SMP Pairing Response */ __u8 prnd[16]; /* SMP Pairing Random */ __u8 pcnf[16]; /* SMP Pairing Confirm */ + __u8 tk[16]; /* SMP Temporary Key */ struct list_head chan_l; rwlock_t chan_lock; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index fa22f4aa3b04..7a9a195c27d3 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.2.3 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 --- include/net/bluetooth/hci.h | 34 ++++++++++++++++++++ include/net/bluetooth/hci_core.h | 6 ++++ net/bluetooth/hci_conn.c | 49 +++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 67 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/smp.c | 16 +++++++--- 5 files changed, 168 insertions(+), 4 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index bd285c6a5509..65345cd215be 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -745,6 +745,33 @@ struct hci_cp_le_conn_update { __le16 max_ce_len; } __packed; +#define HCI_OP_LE_START_ENC 0x2019 +struct hci_cp_le_start_enc { + __le16 handle; + __u8 rand[8]; + __le16 ediv; + __u8 ltk[16]; +} __packed; + +#define HCI_OP_LE_LTK_REPLY 0x201a +struct hci_cp_le_ltk_reply { + __le16 handle; + __u8 ltk[16]; +} __packed; +struct hci_rp_le_ltk_reply { + __u8 status; + __le16 handle; +} __packed; + +#define HCI_OP_LE_LTK_NEG_REPLY 0x201b +struct hci_cp_le_ltk_neg_reply { + __le16 handle; +} __packed; +struct hci_rp_le_ltk_neg_reply { + __u8 status; + __le16 handle; +} __packed; + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 @@ -1035,6 +1062,13 @@ struct hci_ev_le_conn_complete { __u8 clk_accurancy; } __packed; +#define HCI_EV_LE_LTK_REQ 0x05 +struct hci_ev_le_ltk_req { + __le16 handle; + __u8 random[8]; + __le16 ediv; +} __packed; + /* Advertising report event types */ #define ADV_IND 0x00 #define ADV_DIRECT_IND 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7837f3260863..ab6842c6e49a 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -249,6 +249,7 @@ struct hci_conn { __u8 power_save; __u16 disc_timeout; unsigned long pend; + __u8 ltk[16]; __u8 remote_cap; __u8 remote_oob; @@ -861,4 +862,9 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result); void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); +void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], + __u8 ltk[16]); +void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]); +void hci_le_ltk_neg_reply(struct hci_conn *conn); + #endif /* __HCI_CORE_H */ diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 37f5a174f072..18193831bbf1 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 0f643f84131f..166fa113721c 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 7a9a195c27d3..56828db68109 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.2.3 From 88167aed0d2e84364c8f0d9e48429c21fc7894c3 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Thu, 9 Jun 2011 18:50:51 -0300 Subject: Bluetooth: Update the security level when link is encrypted If the pending security level is greater than the current security level and the link is now encrypted, we should update the link security level. This is only useful for LE links, when the only event generated when SMP is sucessful in the Encrypt Change event. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ab6842c6e49a..fe05946c8f0b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -745,6 +745,9 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, if (conn->sec_level == BT_SECURITY_SDP) conn->sec_level = BT_SECURITY_LOW; + if (conn->pending_sec_level > conn->sec_level) + conn->sec_level = conn->pending_sec_level; + hci_proto_encrypt_cfm(conn, status, encrypt); read_lock_bh(&hci_cb_list_lock); -- cgit v1.2.3 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 --- include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/l2cap_core.c | 74 +++++++++++++++++++++++++------------------ net/bluetooth/smp.c | 14 ++++++++ 3 files changed, 60 insertions(+), 30 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 01c993b6b263..157419afe532 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -401,6 +401,8 @@ struct l2cap_conn { __u8 pcnf[16]; /* SMP Pairing Confirm */ __u8 tk[16]; /* SMP Temporary Key */ + struct timer_list security_timer; + struct list_head chan_l; rwlock_t chan_lock; }; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 584a4237eb3f..bbbae2e0aa84 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 dfd6891b9c86..39886786eb7f 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.2.3 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 --- include/net/bluetooth/l2cap.h | 1 + include/net/bluetooth/smp.h | 3 +++ net/bluetooth/smp.c | 54 ++++++++++++++++++++++++++++++++----------- 3 files changed, 45 insertions(+), 13 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 157419afe532..bf1c7f681932 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -400,6 +400,7 @@ struct l2cap_conn { __u8 prnd[16]; /* SMP Pairing Random */ __u8 pcnf[16]; /* SMP Pairing Confirm */ __u8 tk[16]; /* SMP Temporary Key */ + __u8 smp_key_size; struct timer_list security_timer; diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 111853ab239a..4fb7d198a876 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -112,6 +112,9 @@ struct smp_cmd_security_req { #define SMP_UNSPECIFIED 0x08 #define SMP_REPEATED_ATTEMPTS 0x09 +#define SMP_MIN_ENC_KEY_SIZE 7 +#define SMP_MAX_ENC_KEY_SIZE 16 + /* SMP Commands */ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 39886786eb7f..52e9ec2644c1 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.2.3 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 --- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_core.c | 79 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_sock.c | 70 +++-------------------------------- 3 files changed, 87 insertions(+), 64 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index fe05946c8f0b..26233d4d371c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -529,6 +529,8 @@ int hci_inquiry(void __user *arg); struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_blacklist_clear(struct hci_dev *hdev); +int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_uuids_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f62ca1935f5a..8f5bee15e872 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 295e4a88fff8..ff02cf5e77cc 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.2.3 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 --- include/net/bluetooth/mgmt.h | 10 +++++++ net/bluetooth/mgmt.c | 70 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) (limited to 'include/net') diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 4899286ed4e4..45bea25d737f 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -199,6 +199,16 @@ struct mgmt_cp_remove_remote_oob_data { #define MGMT_OP_STOP_DISCOVERY 0x001C +#define MGMT_OP_BLOCK_DEVICE 0x001D +struct mgmt_cp_block_device { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_UNBLOCK_DEVICE 0x001E +struct mgmt_cp_unblock_device { + bdaddr_t bdaddr; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d19208903be4..64c0418a6221 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.2.3 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 --- include/net/bluetooth/l2cap.h | 20 ++++++----- net/bluetooth/l2cap_core.c | 77 ++++++++++++++++++++----------------------- net/bluetooth/l2cap_sock.c | 4 +-- 3 files changed, 48 insertions(+), 53 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index bf1c7f681932..4ab1b27996bc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -324,7 +324,7 @@ struct l2cap_chan { __u16 monitor_timeout; __u16 mps; - __u8 conf_state; + unsigned long conf_state; __u16 conn_state; __u8 next_tx_seq; @@ -424,14 +424,16 @@ struct l2cap_pinfo { struct l2cap_chan *chan; }; -#define L2CAP_CONF_REQ_SENT 0x01 -#define L2CAP_CONF_INPUT_DONE 0x02 -#define L2CAP_CONF_OUTPUT_DONE 0x04 -#define L2CAP_CONF_MTU_DONE 0x08 -#define L2CAP_CONF_MODE_DONE 0x10 -#define L2CAP_CONF_CONNECT_PEND 0x20 -#define L2CAP_CONF_NO_FCS_RECV 0x40 -#define L2CAP_CONF_STATE2_DEVICE 0x80 +enum { + CONF_REQ_SENT, + CONF_INPUT_DONE, + CONF_OUTPUT_DONE, + CONF_MTU_DONE, + CONF_MODE_DONE, + CONF_CONNECT_PEND, + CONF_NO_FCS_RECV, + CONF_STATE2_DEVICE, +}; #define L2CAP_CONF_MAX_CONF_REQ 2 #define L2CAP_CONF_MAX_CONF_RSP 2 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bbbae2e0aa84..b4e927eb174e 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 5c819e002fb1..39082d4e77ce 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.2.3 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 --- include/net/bluetooth/l2cap.h | 26 +++---- net/bluetooth/l2cap_core.c | 161 +++++++++++++++++++----------------------- 2 files changed, 88 insertions(+), 99 deletions(-) (limited to 'include/net') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 4ab1b27996bc..9c18e555b6ed 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -325,7 +325,7 @@ struct l2cap_chan { __u16 mps; unsigned long conf_state; - __u16 conn_state; + unsigned long conn_state; __u8 next_tx_seq; __u8 expected_ack_seq; @@ -438,17 +438,19 @@ enum { #define L2CAP_CONF_MAX_CONF_REQ 2 #define L2CAP_CONF_MAX_CONF_RSP 2 -#define L2CAP_CONN_SAR_SDU 0x0001 -#define L2CAP_CONN_SREJ_SENT 0x0002 -#define L2CAP_CONN_WAIT_F 0x0004 -#define L2CAP_CONN_SREJ_ACT 0x0008 -#define L2CAP_CONN_SEND_PBIT 0x0010 -#define L2CAP_CONN_REMOTE_BUSY 0x0020 -#define L2CAP_CONN_LOCAL_BUSY 0x0040 -#define L2CAP_CONN_REJ_ACT 0x0080 -#define L2CAP_CONN_SEND_FBIT 0x0100 -#define L2CAP_CONN_RNR_SENT 0x0200 -#define L2CAP_CONN_SAR_RETRY 0x0400 +enum { + CONN_SAR_SDU, + CONN_SREJ_SENT, + CONN_WAIT_F, + CONN_SREJ_ACT, + CONN_SEND_PBIT, + CONN_REMOTE_BUSY, + CONN_LOCAL_BUSY, + CONN_REJ_ACT, + CONN_SEND_FBIT, + CONN_RNR_SENT, + CONN_SAR_RETRY, +}; #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b4e927eb174e..25f31f4c7d08 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.2.3