summaryrefslogtreecommitdiff
path: root/net/bluetooth/l2cap_core.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-11-13 08:40:34 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-13 08:40:34 (GMT)
commit42a2d923cc349583ebf6fdd52a7d35e1c2f7e6bd (patch)
tree2b2b0c03b5389c1301800119333967efafd994ca /net/bluetooth/l2cap_core.c
parent5cbb3d216e2041700231bcfc383ee5f8b7fc8b74 (diff)
parent75ecab1df14d90e86cebef9ec5c76befde46e65f (diff)
downloadlinux-fsl-qoriq-42a2d923cc349583ebf6fdd52a7d35e1c2f7e6bd.tar.xz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: 1) The addition of nftables. No longer will we need protocol aware firewall filtering modules, it can all live in userspace. At the core of nftables is a, for lack of a better term, virtual machine that executes byte codes to inspect packet or metadata (arriving interface index, etc.) and make verdict decisions. Besides support for loading packet contents and comparing them, the interpreter supports lookups in various datastructures as fundamental operations. For example sets are supports, and therefore one could create a set of whitelist IP address entries which have ACCEPT verdicts attached to them, and use the appropriate byte codes to do such lookups. Since the interpreted code is composed in userspace, userspace can do things like optimize things before giving it to the kernel. Another major improvement is the capability of atomically updating portions of the ruleset. In the existing netfilter implementation, one has to update the entire rule set in order to make a change and this is very expensive. Userspace tools exist to create nftables rules using existing netfilter rule sets, but both kernel implementations will need to co-exist for quite some time as we transition from the old to the new stuff. Kudos to Patrick McHardy, Pablo Neira Ayuso, and others who have worked so hard on this. 2) Daniel Borkmann and Hannes Frederic Sowa made several improvements to our pseudo-random number generator, mostly used for things like UDP port randomization and netfitler, amongst other things. In particular the taus88 generater is updated to taus113, and test cases are added. 3) Support 64-bit rates in HTB and TBF schedulers, from Eric Dumazet and Yang Yingliang. 4) Add support for new 577xx tigon3 chips to tg3 driver, from Nithin Sujir. 5) Fix two fatal flaws in TCP dynamic right sizing, from Eric Dumazet, Neal Cardwell, and Yuchung Cheng. 6) Allow IP_TOS and IP_TTL to be specified in sendmsg() ancillary control message data, much like other socket option attributes. From Francesco Fusco. 7) Allow applications to specify a cap on the rate computed automatically by the kernel for pacing flows, via a new SO_MAX_PACING_RATE socket option. From Eric Dumazet. 8) Make the initial autotuned send buffer sizing in TCP more closely reflect actual needs, from Eric Dumazet. 9) Currently early socket demux only happens for TCP sockets, but we can do it for connected UDP sockets too. Implementation from Shawn Bohrer. 10) Refactor inet socket demux with the goal of improving hash demux performance for listening sockets. With the main goals being able to use RCU lookups on even request sockets, and eliminating the listening lock contention. From Eric Dumazet. 11) The bonding layer has many demuxes in it's fast path, and an RCU conversion was started back in 3.11, several changes here extend the RCU usage to even more locations. From Ding Tianhong and Wang Yufen, based upon suggestions by Nikolay Aleksandrov and Veaceslav Falico. 12) Allow stackability of segmentation offloads to, in particular, allow segmentation offloading over tunnels. From Eric Dumazet. 13) Significantly improve the handling of secret keys we input into the various hash functions in the inet hashtables, TCP fast open, as well as syncookies. From Hannes Frederic Sowa. The key fundamental operation is "net_get_random_once()" which uses static keys. Hannes even extended this to ipv4/ipv6 fragmentation handling and our generic flow dissector. 14) The generic driver layer takes care now to set the driver data to NULL on device removal, so it's no longer necessary for drivers to explicitly set it to NULL any more. Many drivers have been cleaned up in this way, from Jingoo Han. 15) Add a BPF based packet scheduler classifier, from Daniel Borkmann. 16) Improve CRC32 interfaces and generic SKB checksum iterators so that SCTP's checksumming can more cleanly be handled. Also from Daniel Borkmann. 17) Add a new PMTU discovery mode, IP_PMTUDISC_INTERFACE, which forces using the interface MTU value. This helps avoid PMTU attacks, particularly on DNS servers. From Hannes Frederic Sowa. 18) Use generic XPS for transmit queue steering rather than internal (re-)implementation in virtio-net. From Jason Wang. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1622 commits) random32: add test cases for taus113 implementation random32: upgrade taus88 generator to taus113 from errata paper random32: move rnd_state to linux/random.h random32: add prandom_reseed_late() and call when nonblocking pool becomes initialized random32: add periodic reseeding random32: fix off-by-one in seeding requirement PHY: Add RTL8201CP phy_driver to realtek xtsonic: add missing platform_set_drvdata() in xtsonic_probe() macmace: add missing platform_set_drvdata() in mace_probe() ethernet/arc/arc_emac: add missing platform_set_drvdata() in arc_emac_probe() ipv6: protect for_each_sk_fl_rcu in mem_check with rcu_read_lock_bh vlan: Implement vlan_dev_get_egress_qos_mask as an inline. ixgbe: add warning when max_vfs is out of range. igb: Update link modes display in ethtool netfilter: push reasm skb through instead of original frag skbs ip6_output: fragment outgoing reassembled skb properly MAINTAINERS: mv643xx_eth: take over maintainership from Lennart net_sched: tbf: support of 64bit rates ixgbe: deleting dfwd stations out of order can cause null ptr deref ixgbe: fix build err, num_rx_queues is only available with CONFIG_RPS ...
Diffstat (limited to 'net/bluetooth/l2cap_core.c')
-rw-r--r--net/bluetooth/l2cap_core.c538
1 files changed, 267 insertions, 271 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 63fa111..0cef677 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -36,14 +36,15 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/smp.h>
-#include <net/bluetooth/a2mp.h>
-#include <net/bluetooth/amp.h>
+
+#include "smp.h"
+#include "a2mp.h"
+#include "amp.h"
bool disable_ertm;
-static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
-static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, };
+static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
+static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
@@ -58,6 +59,18 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
struct sk_buff_head *skbs, u8 event);
+static inline __u8 bdaddr_type(struct hci_conn *hcon, __u8 type)
+{
+ if (hcon->type == LE_LINK) {
+ if (type == ADDR_LE_DEV_PUBLIC)
+ return BDADDR_LE_PUBLIC;
+ else
+ return BDADDR_LE_RANDOM;
+ }
+
+ return BDADDR_BREDR;
+}
+
/* ---- L2CAP channels ---- */
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
@@ -148,7 +161,7 @@ static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
struct l2cap_chan *c;
list_for_each_entry(c, &chan_list, global_l) {
- if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src))
+ if (c->sport == psm && !bacmp(&c->src, src))
return c;
}
return NULL;
@@ -210,38 +223,25 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
return 0;
}
-static void __l2cap_state_change(struct l2cap_chan *chan, int state)
+static void l2cap_state_change(struct l2cap_chan *chan, int state)
{
BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
state_to_string(state));
chan->state = state;
- chan->ops->state_change(chan, state);
-}
-
-static void l2cap_state_change(struct l2cap_chan *chan, int state)
-{
- struct sock *sk = chan->sk;
-
- lock_sock(sk);
- __l2cap_state_change(chan, state);
- release_sock(sk);
+ chan->ops->state_change(chan, state, 0);
}
-static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err)
+static inline void l2cap_state_change_and_error(struct l2cap_chan *chan,
+ int state, int err)
{
- struct sock *sk = chan->sk;
-
- sk->sk_err = err;
+ chan->state = state;
+ chan->ops->state_change(chan, chan->state, err);
}
static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err)
{
- struct sock *sk = chan->sk;
-
- lock_sock(sk);
- __l2cap_chan_set_err(chan, err);
- release_sock(sk);
+ chan->ops->state_change(chan, chan->state, err);
}
static void __set_retrans_timer(struct l2cap_chan *chan)
@@ -620,10 +620,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
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 %s sk %p", chan, state_to_string(chan->state),
- sk);
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
switch (chan->state) {
case BT_LISTEN:
@@ -634,7 +632,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
case BT_CONFIG:
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
conn->hcon->type == ACL_LINK) {
- __set_chan_timer(chan, sk->sk_sndtimeo);
+ __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
l2cap_send_disconn_req(chan, reason);
} else
l2cap_chan_del(chan, reason);
@@ -646,10 +644,11 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
struct l2cap_conn_rsp rsp;
__u16 result;
- if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
result = L2CAP_CR_SEC_BLOCK;
else
result = L2CAP_CR_BAD_PSM;
+
l2cap_state_change(chan, BT_DISCONN);
rsp.scid = cpu_to_le16(chan->dcid);
@@ -676,7 +675,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
{
- if (chan->chan_type == L2CAP_CHAN_RAW) {
+ switch (chan->chan_type) {
+ case L2CAP_CHAN_RAW:
switch (chan->sec_level) {
case BT_SECURITY_HIGH:
return HCI_AT_DEDICATED_BONDING_MITM;
@@ -685,15 +685,29 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
default:
return HCI_AT_NO_BONDING;
}
- } else if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) {
- if (chan->sec_level == BT_SECURITY_LOW)
- chan->sec_level = BT_SECURITY_SDP;
-
+ break;
+ case L2CAP_CHAN_CONN_LESS:
+ if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_3DSP)) {
+ if (chan->sec_level == BT_SECURITY_LOW)
+ chan->sec_level = BT_SECURITY_SDP;
+ }
if (chan->sec_level == BT_SECURITY_HIGH)
return HCI_AT_NO_BONDING_MITM;
else
return HCI_AT_NO_BONDING;
- } else {
+ break;
+ case L2CAP_CHAN_CONN_ORIENTED:
+ if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) {
+ if (chan->sec_level == BT_SECURITY_LOW)
+ chan->sec_level = BT_SECURITY_SDP;
+
+ if (chan->sec_level == BT_SECURITY_HIGH)
+ return HCI_AT_NO_BONDING_MITM;
+ else
+ return HCI_AT_NO_BONDING;
+ }
+ /* fall through */
+ default:
switch (chan->sec_level) {
case BT_SECURITY_HIGH:
return HCI_AT_GENERAL_BONDING_MITM;
@@ -702,6 +716,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
default:
return HCI_AT_NO_BONDING;
}
+ break;
}
}
@@ -1015,14 +1030,29 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
static bool __amp_capable(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
+ struct hci_dev *hdev;
+ bool amp_available = false;
- if (enable_hs &&
- hci_amp_capable() &&
- chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED &&
- conn->fixed_chan_mask & L2CAP_FC_A2MP)
- return true;
- else
+ if (!conn->hs_enabled)
return false;
+
+ if (!(conn->fixed_chan_mask & L2CAP_FC_A2MP))
+ return false;
+
+ read_lock(&hci_dev_list_lock);
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ if (hdev->amp_type != AMP_TYPE_BREDR &&
+ test_bit(HCI_UP, &hdev->flags)) {
+ amp_available = true;
+ break;
+ }
+ }
+ read_unlock(&hci_dev_list_lock);
+
+ if (chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED)
+ return amp_available;
+
+ return false;
}
static bool l2cap_check_efs(struct l2cap_chan *chan)
@@ -1186,7 +1216,6 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
struct l2cap_disconn_req req;
@@ -1209,10 +1238,7 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ,
sizeof(req), &req);
- lock_sock(sk);
- __l2cap_state_change(chan, BT_DISCONN);
- __l2cap_chan_set_err(chan, err);
- release_sock(sk);
+ l2cap_state_change_and_error(chan, BT_DISCONN, err);
}
/* ---- L2CAP connections ---- */
@@ -1225,8 +1251,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
mutex_lock(&conn->chan_lock);
list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
-
l2cap_chan_lock(chan);
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1258,19 +1282,16 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
rsp.dcid = cpu_to_le16(chan->scid);
if (l2cap_chan_check_security(chan)) {
- lock_sock(sk);
- if (test_bit(BT_SK_DEFER_SETUP,
- &bt_sk(sk)->flags)) {
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
chan->ops->defer(chan);
} else {
- __l2cap_state_change(chan, BT_CONFIG);
+ l2cap_state_change(chan, BT_CONFIG);
rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
}
- release_sock(sk);
} else {
rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
@@ -1309,8 +1330,6 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
- struct sock *sk = c->sk;
-
if (state && c->state != state)
continue;
@@ -1319,16 +1338,16 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
int src_any, dst_any;
/* Exact match. */
- src_match = !bacmp(&bt_sk(sk)->src, src);
- dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+ src_match = !bacmp(&c->src, src);
+ dst_match = !bacmp(&c->dst, dst);
if (src_match && dst_match) {
read_unlock(&chan_list_lock);
return c;
}
/* Closest match */
- src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
- dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+ src_any = !bacmp(&c->src, BDADDR_ANY);
+ dst_any = !bacmp(&c->dst, BDADDR_ANY);
if ((src_match && dst_any) || (src_any && dst_match) ||
(src_any && dst_any))
c1 = c;
@@ -1342,14 +1361,15 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
{
- struct sock *parent;
+ struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan, *pchan;
+ u8 dst_type;
BT_DBG("");
/* Check if we have socket listening on cid */
pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
- conn->src, conn->dst);
+ &hcon->src, &hcon->dst);
if (!pchan)
return;
@@ -1357,9 +1377,13 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
return;
- parent = pchan->sk;
+ dst_type = bdaddr_type(hcon, hcon->dst_type);
+
+ /* If device is blocked, do not create a channel for it */
+ if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type))
+ return;
- lock_sock(parent);
+ l2cap_chan_lock(pchan);
chan = pchan->ops->new_connection(pchan);
if (!chan)
@@ -1367,13 +1391,15 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
chan->dcid = L2CAP_CID_ATT;
- bacpy(&bt_sk(chan->sk)->src, conn->src);
- bacpy(&bt_sk(chan->sk)->dst, conn->dst);
+ bacpy(&chan->src, &hcon->src);
+ bacpy(&chan->dst, &hcon->dst);
+ chan->src_type = bdaddr_type(hcon, hcon->src_type);
+ chan->dst_type = dst_type;
__l2cap_chan_add(conn, chan);
clean:
- release_sock(parent);
+ l2cap_chan_unlock(pchan);
}
static void l2cap_conn_ready(struct l2cap_conn *conn)
@@ -1408,12 +1434,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_chan_ready(chan);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
- struct sock *sk = chan->sk;
- __clear_chan_timer(chan);
- lock_sock(sk);
- __l2cap_state_change(chan, BT_CONNECTED);
- sk->sk_state_change(sk);
- release_sock(sk);
+ l2cap_chan_ready(chan);
} else if (chan->state == BT_CONNECT) {
l2cap_do_start(chan);
@@ -1633,11 +1654,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
break;
}
- conn->src = &hcon->hdev->bdaddr;
- conn->dst = &hcon->dst;
-
conn->feat_mask = 0;
+ if (hcon->type == ACL_LINK)
+ conn->hs_enabled = test_bit(HCI_HS_ENABLED,
+ &hcon->hdev->dev_flags);
+
spin_lock_init(&conn->lock);
mutex_init(&conn->chan_lock);
@@ -1688,8 +1710,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
- struct sock *sk = c->sk;
-
if (state && c->state != state)
continue;
@@ -1698,16 +1718,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
int src_any, dst_any;
/* Exact match. */
- src_match = !bacmp(&bt_sk(sk)->src, src);
- dst_match = !bacmp(&bt_sk(sk)->dst, dst);
+ src_match = !bacmp(&c->src, src);
+ dst_match = !bacmp(&c->dst, dst);
if (src_match && dst_match) {
read_unlock(&chan_list_lock);
return c;
}
/* Closest match */
- src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY);
- dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY);
+ src_any = !bacmp(&c->src, BDADDR_ANY);
+ dst_any = !bacmp(&c->dst, BDADDR_ANY);
if ((src_match && dst_any) || (src_any && dst_match) ||
(src_any && dst_any))
c1 = c;
@@ -1722,18 +1742,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bdaddr_t *dst, u8 dst_type)
{
- struct sock *sk = chan->sk;
- bdaddr_t *src = &bt_sk(sk)->src;
struct l2cap_conn *conn;
struct hci_conn *hcon;
struct hci_dev *hdev;
__u8 auth_type;
int err;
- BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", src, dst,
+ BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
dst_type, __le16_to_cpu(psm));
- hdev = hci_get_route(dst, src);
+ hdev = hci_get_route(dst, &chan->src);
if (!hdev)
return -EHOSTUNREACH;
@@ -1790,9 +1808,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
}
/* Set destination address and psm */
- lock_sock(sk);
- bacpy(&bt_sk(sk)->dst, dst);
- release_sock(sk);
+ bacpy(&chan->dst, dst);
+ chan->dst_type = dst_type;
chan->psm = psm;
chan->dcid = cid;
@@ -1825,7 +1842,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
}
/* Update source addr of the socket */
- bacpy(src, conn->src);
+ bacpy(&chan->src, &hcon->src);
+ chan->src_type = bdaddr_type(hcon, hcon->src_type);
l2cap_chan_unlock(chan);
l2cap_chan_add(conn, chan);
@@ -1835,7 +1853,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
hci_conn_drop(hcon);
l2cap_state_change(chan, BT_CONNECT);
- __set_chan_timer(chan, sk->sk_sndtimeo);
+ __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
if (hcon->state == BT_CONNECTED) {
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
@@ -1855,38 +1873,6 @@ done:
return err;
}
-int __l2cap_wait_ack(struct sock *sk)
-{
- struct l2cap_chan *chan = l2cap_pi(sk)->chan;
- DECLARE_WAITQUEUE(wait, current);
- int err = 0;
- int timeo = HZ/5;
-
- add_wait_queue(sk_sleep(sk), &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- while (chan->unacked_frames > 0 && chan->conn) {
- if (!timeo)
- timeo = HZ/5;
-
- if (signal_pending(current)) {
- err = sock_intr_errno(timeo);
- break;
- }
-
- release_sock(sk);
- timeo = schedule_timeout(timeo);
- lock_sock(sk);
- set_current_state(TASK_INTERRUPTIBLE);
-
- err = sock_error(sk);
- if (err)
- break;
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(sk_sleep(sk), &wait);
- return err;
-}
-
static void l2cap_monitor_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -2263,7 +2249,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE;
struct l2cap_hdr *lh;
- BT_DBG("chan %p len %zu priority %u", chan, len, priority);
+ BT_DBG("chan %p psm 0x%2.2x len %zu priority %u", chan,
+ __le16_to_cpu(chan->psm), len, priority);
count = min_t(unsigned int, (conn->mtu - hlen), len);
@@ -2278,7 +2265,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan,
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->cid = cpu_to_le16(chan->dcid);
lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE);
- put_unaligned(chan->psm, skb_put(skb, L2CAP_PSMLEN_SIZE));
+ put_unaligned(chan->psm, (__le16 *) skb_put(skb, L2CAP_PSMLEN_SIZE));
err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
if (unlikely(err < 0)) {
@@ -2826,17 +2813,16 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
mutex_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) {
- struct sock *sk = chan->sk;
if (chan->chan_type != L2CAP_CHAN_RAW)
continue;
- /* Don't send frame to the socket it came from */
- if (skb->sk == sk)
+ /* Don't send frame to the channel it came from */
+ if (bt_cb(skb)->chan == chan)
continue;
+
nskb = skb_clone(skb, GFP_KERNEL);
if (!nskb)
continue;
-
if (chan->ops->recv(chan, nskb))
kfree_skb(nskb);
}
@@ -3043,8 +3029,8 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
skb_queue_head_init(&chan->tx_q);
- chan->local_amp_id = 0;
- chan->move_id = 0;
+ chan->local_amp_id = AMP_ID_BREDR;
+ chan->move_id = AMP_ID_BREDR;
chan->move_state = L2CAP_MOVE_STABLE;
chan->move_role = L2CAP_MOVE_ROLE_NONE;
@@ -3084,20 +3070,20 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
}
}
-static inline bool __l2cap_ews_supported(struct l2cap_chan *chan)
+static inline bool __l2cap_ews_supported(struct l2cap_conn *conn)
{
- return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
+ return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
}
-static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
+static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
{
- return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
+ return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
}
static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
struct l2cap_conf_rfc *rfc)
{
- if (chan->local_amp_id && chan->hs_hcon) {
+ if (chan->local_amp_id != AMP_ID_BREDR && chan->hs_hcon) {
u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to;
/* Class 1 devices have must have ERTM timeouts
@@ -3135,7 +3121,7 @@ static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
{
if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
- __l2cap_ews_supported(chan)) {
+ __l2cap_ews_supported(chan->conn)) {
/* use extended control field */
set_bit(FLAG_EXT_CTRL, &chan->flags);
chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
@@ -3165,7 +3151,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state))
break;
- if (__l2cap_efs_supported(chan))
+ if (__l2cap_efs_supported(chan->conn))
set_bit(FLAG_EFS_ENABLE, &chan->flags);
/* fall through */
@@ -3317,7 +3303,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
break;
case L2CAP_CONF_EWS:
- if (!enable_hs)
+ if (!chan->conn->hs_enabled)
return -ECONNREFUSED;
set_bit(FLAG_EXT_CTRL, &chan->flags);
@@ -3349,7 +3335,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
}
if (remote_efs) {
- if (__l2cap_efs_supported(chan))
+ if (__l2cap_efs_supported(chan->conn))
set_bit(FLAG_EFS_ENABLE, &chan->flags);
else
return -ECONNREFUSED;
@@ -3715,7 +3701,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
struct l2cap_chan *chan = NULL, *pchan;
- struct sock *parent, *sk = NULL;
int result, status = L2CAP_CS_NO_INFO;
u16 dcid = 0, scid = __le16_to_cpu(req->scid);
@@ -3724,16 +3709,15 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
BT_DBG("psm 0x%2.2x scid 0x%4.4x", __le16_to_cpu(psm), scid);
/* Check if we have socket listening on psm */
- pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src, conn->dst);
+ pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
+ &conn->hcon->dst);
if (!pchan) {
result = L2CAP_CR_BAD_PSM;
goto sendresp;
}
- parent = pchan->sk;
-
mutex_lock(&conn->chan_lock);
- lock_sock(parent);
+ l2cap_chan_lock(pchan);
/* Check if the ACL is secure enough (if not SDP) */
if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) &&
@@ -3753,8 +3737,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
if (!chan)
goto response;
- sk = chan->sk;
-
/* For certain devices (ex: HID mouse), support for authentication,
* pairing and bonding is optional. For such devices, inorder to avoid
* the ACL alive for too long after L2CAP disconnection, reset the ACL
@@ -3762,8 +3744,10 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
*/
conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
- bacpy(&bt_sk(sk)->src, conn->src);
- bacpy(&bt_sk(sk)->dst, conn->dst);
+ bacpy(&chan->src, &conn->hcon->src);
+ bacpy(&chan->dst, &conn->hcon->dst);
+ chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
+ chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type);
chan->psm = psm;
chan->dcid = scid;
chan->local_amp_id = amp_id;
@@ -3772,14 +3756,14 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
dcid = chan->scid;
- __set_chan_timer(chan, sk->sk_sndtimeo);
+ __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
chan->ident = cmd->ident;
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
if (l2cap_chan_check_security(chan)) {
- if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
- __l2cap_state_change(chan, BT_CONNECT2);
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan);
@@ -3788,28 +3772,28 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
* The connection will succeed after the
* physical link is up.
*/
- if (amp_id) {
- __l2cap_state_change(chan, BT_CONNECT2);
- result = L2CAP_CR_PEND;
- } else {
- __l2cap_state_change(chan, BT_CONFIG);
+ if (amp_id == AMP_ID_BREDR) {
+ l2cap_state_change(chan, BT_CONFIG);
result = L2CAP_CR_SUCCESS;
+ } else {
+ l2cap_state_change(chan, BT_CONNECT2);
+ result = L2CAP_CR_PEND;
}
status = L2CAP_CS_NO_INFO;
}
} else {
- __l2cap_state_change(chan, BT_CONNECT2);
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHEN_PEND;
}
} else {
- __l2cap_state_change(chan, BT_CONNECT2);
+ l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_NO_INFO;
}
response:
- release_sock(parent);
+ l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
sendresp:
@@ -3891,13 +3875,13 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
if (scid) {
chan = __l2cap_get_chan_by_scid(conn, scid);
if (!chan) {
- err = -EFAULT;
+ err = -EBADSLT;
goto unlock;
}
} else {
chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
if (!chan) {
- err = -EFAULT;
+ err = -EBADSLT;
goto unlock;
}
}
@@ -3965,6 +3949,18 @@ static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data,
L2CAP_CONF_SUCCESS, flags), data);
}
+static void cmd_reject_invalid_cid(struct l2cap_conn *conn, u8 ident,
+ u16 scid, u16 dcid)
+{
+ struct l2cap_cmd_rej_cid rej;
+
+ rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
+ rej.scid = __cpu_to_le16(scid);
+ rej.dcid = __cpu_to_le16(dcid);
+
+ l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
+}
+
static inline int l2cap_config_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
@@ -3984,18 +3980,14 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
chan = l2cap_get_chan_by_scid(conn, dcid);
- if (!chan)
- return -ENOENT;
+ if (!chan) {
+ cmd_reject_invalid_cid(conn, cmd->ident, dcid, 0);
+ return 0;
+ }
if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) {
- struct l2cap_cmd_rej_cid rej;
-
- rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID);
- rej.scid = cpu_to_le16(chan->scid);
- rej.dcid = cpu_to_le16(chan->dcid);
-
- l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
- sizeof(rej), &rej);
+ cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+ chan->dcid);
goto unlock;
}
@@ -4198,7 +4190,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
struct l2cap_disconn_rsp rsp;
u16 dcid, scid;
struct l2cap_chan *chan;
- struct sock *sk;
if (cmd_len != sizeof(*req))
return -EPROTO;
@@ -4213,20 +4204,17 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
chan = __l2cap_get_chan_by_scid(conn, dcid);
if (!chan) {
mutex_unlock(&conn->chan_lock);
+ cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid);
return 0;
}
l2cap_chan_lock(chan);
- sk = chan->sk;
-
rsp.dcid = cpu_to_le16(chan->scid);
rsp.scid = cpu_to_le16(chan->dcid);
l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp);
- lock_sock(sk);
- sk->sk_shutdown = SHUTDOWN_MASK;
- release_sock(sk);
+ chan->ops->set_shutdown(chan);
l2cap_chan_hold(chan);
l2cap_chan_del(chan, ECONNRESET);
@@ -4303,7 +4291,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
if (!disable_ertm)
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
| L2CAP_FEAT_FCS;
- if (enable_hs)
+ if (conn->hs_enabled)
feat_mask |= L2CAP_FEAT_EXT_FLOW
| L2CAP_FEAT_EXT_WINDOW;
@@ -4314,7 +4302,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
u8 buf[12];
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
- if (enable_hs)
+ if (conn->hs_enabled)
l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
else
l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
@@ -4411,7 +4399,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
if (cmd_len != sizeof(*req))
return -EPROTO;
- if (!enable_hs)
+ if (!conn->hs_enabled)
return -EINVAL;
psm = le16_to_cpu(req->psm);
@@ -4420,7 +4408,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
/* For controller id 0 make BR/EDR connection */
- if (req->amp_id == HCI_BREDR_ID) {
+ if (req->amp_id == AMP_ID_BREDR) {
l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
req->amp_id);
return 0;
@@ -4442,10 +4430,13 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
struct amp_mgr *mgr = conn->hcon->amp_mgr;
struct hci_conn *hs_hcon;
- hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
+ hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK,
+ &conn->hcon->dst);
if (!hs_hcon) {
hci_dev_put(hdev);
- return -EFAULT;
+ cmd_reject_invalid_cid(conn, cmd->ident, chan->scid,
+ chan->dcid);
+ return 0;
}
BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
@@ -4469,7 +4460,7 @@ error:
l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
sizeof(rsp), &rsp);
- return -EFAULT;
+ return 0;
}
static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
@@ -4655,7 +4646,7 @@ void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
if (chan->state != BT_CONNECTED) {
/* Ignore logical link if channel is on BR/EDR */
- if (chan->local_amp_id)
+ if (chan->local_amp_id != AMP_ID_BREDR)
l2cap_logical_finish_create(chan, hchan);
} else {
l2cap_logical_finish_move(chan, hchan);
@@ -4666,7 +4657,7 @@ void l2cap_move_start(struct l2cap_chan *chan)
{
BT_DBG("chan %p", chan);
- if (chan->local_amp_id == HCI_BREDR_ID) {
+ if (chan->local_amp_id == AMP_ID_BREDR) {
if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
return;
chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
@@ -4723,7 +4714,7 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result,
sizeof(rsp), &rsp);
if (result == L2CAP_CR_SUCCESS) {
- __l2cap_state_change(chan, BT_CONFIG);
+ l2cap_state_change(chan, BT_CONFIG);
set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
L2CAP_CONF_REQ,
@@ -4838,7 +4829,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
- if (!enable_hs)
+ if (!conn->hs_enabled)
return -EINVAL;
chan = l2cap_get_chan_by_dcid(conn, icid);
@@ -4865,7 +4856,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
goto send_move_response;
}
- if (req->dest_amp_id) {
+ if (req->dest_amp_id != AMP_ID_BREDR) {
struct hci_dev *hdev;
hdev = hci_dev_get(req->dest_amp_id);
if (!hdev || hdev->dev_type != HCI_AMP ||
@@ -4885,7 +4876,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
*/
if ((__chan_is_moving(chan) ||
chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
- bacmp(conn->src, conn->dst) > 0) {
+ bacmp(&conn->hcon->src, &conn->hcon->dst) > 0) {
result = L2CAP_MR_COLLISION;
goto send_move_response;
}
@@ -4895,7 +4886,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
chan->move_id = req->dest_amp_id;
icid = chan->dcid;
- if (!req->dest_amp_id) {
+ if (req->dest_amp_id == AMP_ID_BREDR) {
/* Moving to BR/EDR */
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
@@ -5087,7 +5078,7 @@ static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
if (result == L2CAP_MC_CONFIRMED) {
chan->local_amp_id = chan->move_id;
- if (!chan->local_amp_id)
+ if (chan->local_amp_id == AMP_ID_BREDR)
__release_logical_link(chan);
} else {
chan->move_id = chan->local_amp_id;
@@ -5127,7 +5118,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
chan->local_amp_id = chan->move_id;
- if (!chan->local_amp_id && chan->hs_hchan)
+ if (chan->local_amp_id == AMP_ID_BREDR && chan->hs_hchan)
__release_logical_link(chan);
l2cap_move_done(chan);
@@ -5219,7 +5210,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
case L2CAP_CONN_RSP:
case L2CAP_CREATE_CHAN_RSP:
- err = l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
+ l2cap_connect_create_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_CONF_REQ:
@@ -5227,7 +5218,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break;
case L2CAP_CONF_RSP:
- err = l2cap_config_rsp(conn, cmd, cmd_len, data);
+ l2cap_config_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_DISCONN_REQ:
@@ -5235,7 +5226,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break;
case L2CAP_DISCONN_RSP:
- err = l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
+ l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_ECHO_REQ:
@@ -5250,7 +5241,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break;
case L2CAP_INFO_RSP:
- err = l2cap_information_rsp(conn, cmd, cmd_len, data);
+ l2cap_information_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_CREATE_CHAN_REQ:
@@ -5262,7 +5253,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break;
case L2CAP_MOVE_CHAN_RSP:
- err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
+ l2cap_move_channel_rsp(conn, cmd, cmd_len, data);
break;
case L2CAP_MOVE_CHAN_CFM:
@@ -5270,7 +5261,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break;
case L2CAP_MOVE_CHAN_CFM_RSP:
- err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
+ l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data);
break;
default:
@@ -5304,51 +5295,48 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
struct sk_buff *skb)
{
- u8 *data = skb->data;
- int len = skb->len;
- struct l2cap_cmd_hdr cmd;
+ struct hci_conn *hcon = conn->hcon;
+ struct l2cap_cmd_hdr *cmd;
+ u16 len;
int err;
- l2cap_raw_recv(conn, skb);
+ if (hcon->type != LE_LINK)
+ goto drop;
- while (len >= L2CAP_CMD_HDR_SIZE) {
- u16 cmd_len;
- memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
- data += L2CAP_CMD_HDR_SIZE;
- len -= L2CAP_CMD_HDR_SIZE;
+ if (skb->len < L2CAP_CMD_HDR_SIZE)
+ goto drop;
- cmd_len = le16_to_cpu(cmd.len);
+ cmd = (void *) skb->data;
+ skb_pull(skb, L2CAP_CMD_HDR_SIZE);
- BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
- cmd.ident);
+ len = le16_to_cpu(cmd->len);
- if (cmd_len > len || !cmd.ident) {
- BT_DBG("corrupted command");
- break;
- }
+ BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd->code, len, cmd->ident);
- err = l2cap_le_sig_cmd(conn, &cmd, data);
- if (err) {
- struct l2cap_cmd_rej_unk rej;
+ if (len != skb->len || !cmd->ident) {
+ BT_DBG("corrupted command");
+ goto drop;
+ }
- BT_ERR("Wrong link type (%d)", err);
+ err = l2cap_le_sig_cmd(conn, cmd, skb->data);
+ if (err) {
+ struct l2cap_cmd_rej_unk rej;
- /* FIXME: Map err to a valid reason */
- rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
- l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
- sizeof(rej), &rej);
- }
+ BT_ERR("Wrong link type (%d)", err);
- data += cmd_len;
- len -= cmd_len;
+ rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
+ sizeof(rej), &rej);
}
+drop:
kfree_skb(skb);
}
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
struct sk_buff *skb)
{
+ struct hci_conn *hcon = conn->hcon;
u8 *data = skb->data;
int len = skb->len;
struct l2cap_cmd_hdr cmd;
@@ -5356,6 +5344,9 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
l2cap_raw_recv(conn, skb);
+ if (hcon->type != ACL_LINK)
+ goto drop;
+
while (len >= L2CAP_CMD_HDR_SIZE) {
u16 cmd_len;
memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
@@ -5378,7 +5369,6 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
BT_ERR("Wrong link type (%d)", err);
- /* FIXME: Map err to a valid reason */
rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
sizeof(rej), &rej);
@@ -5388,6 +5378,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
len -= cmd_len;
}
+drop:
kfree_skb(skb);
}
@@ -5784,7 +5775,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
struct sk_buff *skb, u8 event)
{
int err = 0;
- bool skb_in_use = 0;
+ bool skb_in_use = false;
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
event);
@@ -5805,7 +5796,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
control->txseq);
chan->buffer_seq = chan->expected_tx_seq;
- skb_in_use = 1;
+ skb_in_use = true;
err = l2cap_reassemble_sdu(chan, skb, control);
if (err)
@@ -5841,7 +5832,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
* current frame is stored for later use.
*/
skb_queue_tail(&chan->srej_q, skb);
- skb_in_use = 1;
+ skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
@@ -5919,7 +5910,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
{
int err = 0;
u16 txseq = control->txseq;
- bool skb_in_use = 0;
+ bool skb_in_use = false;
BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
event);
@@ -5931,7 +5922,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
/* Keep frame for reassembly later */
l2cap_pass_to_tx(chan, control);
skb_queue_tail(&chan->srej_q, skb);
- skb_in_use = 1;
+ skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
@@ -5942,7 +5933,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
l2cap_pass_to_tx(chan, control);
skb_queue_tail(&chan->srej_q, skb);
- skb_in_use = 1;
+ skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
@@ -5957,7 +5948,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
* the missing frames.
*/
skb_queue_tail(&chan->srej_q, skb);
- skb_in_use = 1;
+ skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
@@ -5971,7 +5962,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
* SREJ'd frames.
*/
skb_queue_tail(&chan->srej_q, skb);
- skb_in_use = 1;
+ skb_in_use = true;
BT_DBG("Queued %p (queue len %d)", skb,
skb_queue_len(&chan->srej_q));
@@ -6380,9 +6371,13 @@ done:
static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
struct sk_buff *skb)
{
+ struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan;
- chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst);
+ if (hcon->type != ACL_LINK)
+ goto drop;
+
+ chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst);
if (!chan)
goto drop;
@@ -6394,6 +6389,10 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
if (chan->imtu < skb->len)
goto drop;
+ /* Store remote BD_ADDR and PSM for msg_name */
+ bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
+ bt_cb(skb)->psm = psm;
+
if (!chan->ops->recv(chan, skb))
return;
@@ -6404,15 +6403,22 @@ drop:
static void l2cap_att_channel(struct l2cap_conn *conn,
struct sk_buff *skb)
{
+ struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan;
+ if (hcon->type != LE_LINK)
+ goto drop;
+
chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
- conn->src, conn->dst);
+ &hcon->src, &hcon->dst);
if (!chan)
goto drop;
BT_DBG("chan %p, len %d", chan, skb->len);
+ if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type))
+ goto drop;
+
if (chan->imtu < skb->len)
goto drop;
@@ -6441,9 +6447,6 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("len %d, cid 0x%4.4x", len, cid);
switch (cid) {
- case L2CAP_CID_LE_SIGNALING:
- l2cap_le_sig_channel(conn, skb);
- break;
case L2CAP_CID_SIGNALING:
l2cap_sig_channel(conn, skb);
break;
@@ -6458,6 +6461,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
l2cap_att_channel(conn, skb);
break;
+ case L2CAP_CID_LE_SIGNALING:
+ l2cap_le_sig_channel(conn, skb);
+ break;
+
case L2CAP_CID_SMP:
if (smp_sig_channel(conn, skb))
l2cap_conn_del(conn->hcon, EACCES);
@@ -6481,17 +6488,15 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
/* Find listening sockets and check their link_mode */
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
- struct sock *sk = c->sk;
-
if (c->state != BT_LISTEN)
continue;
- if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
+ if (!bacmp(&c->src, &hdev->bdaddr)) {
lm1 |= HCI_LM_ACCEPT;
if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
lm1 |= HCI_LM_MASTER;
exact++;
- } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
+ } else if (!bacmp(&c->src, BDADDR_ANY)) {
lm2 |= HCI_LM_ACCEPT;
if (test_bit(FLAG_ROLE_SWITCH, &c->flags))
lm2 |= HCI_LM_MASTER;
@@ -6597,11 +6602,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (!status && (chan->state == BT_CONNECTED ||
chan->state == BT_CONFIG)) {
- struct sock *sk = chan->sk;
-
- clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
- sk->sk_state_change(sk);
-
+ chan->ops->resume(chan);
l2cap_check_encryption(chan, encrypt);
l2cap_chan_unlock(chan);
continue;
@@ -6614,32 +6615,26 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
}
} else if (chan->state == BT_CONNECT2) {
- struct sock *sk = chan->sk;
struct l2cap_conn_rsp rsp;
__u16 res, stat;
- lock_sock(sk);
-
if (!status) {
- if (test_bit(BT_SK_DEFER_SETUP,
- &bt_sk(sk)->flags)) {
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
res = L2CAP_CR_PEND;
stat = L2CAP_CS_AUTHOR_PEND;
chan->ops->defer(chan);
} else {
- __l2cap_state_change(chan, BT_CONFIG);
+ l2cap_state_change(chan, BT_CONFIG);
res = L2CAP_CR_SUCCESS;
stat = L2CAP_CS_NO_INFO;
}
} else {
- __l2cap_state_change(chan, BT_DISCONN);
+ l2cap_state_change(chan, BT_DISCONN);
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
res = L2CAP_CR_SEC_BLOCK;
stat = L2CAP_CS_NO_INFO;
}
- release_sock(sk);
-
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = cpu_to_le16(res);
@@ -6756,9 +6751,13 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
conn->rx_len -= skb->len;
if (!conn->rx_len) {
- /* Complete frame received */
- l2cap_recv_frame(conn, conn->rx_skb);
+ /* Complete frame received. l2cap_recv_frame
+ * takes ownership of the skb so set the global
+ * rx_skb pointer to NULL first.
+ */
+ struct sk_buff *rx_skb = conn->rx_skb;
conn->rx_skb = NULL;
+ l2cap_recv_frame(conn, rx_skb);
}
break;
}
@@ -6775,10 +6774,8 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
read_lock(&chan_list_lock);
list_for_each_entry(c, &chan_list, global_l) {
- struct sock *sk = c->sk;
-
seq_printf(f, "%pMR %pMR %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
- &bt_sk(sk)->src, &bt_sk(sk)->dst,
+ &c->src, &c->dst,
c->state, __le16_to_cpu(c->psm),
c->scid, c->dcid, c->imtu, c->omtu,
c->sec_level, c->mode);
@@ -6811,12 +6808,11 @@ int __init l2cap_init(void)
if (err < 0)
return err;
- if (bt_debugfs) {
- l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
- NULL, &l2cap_debugfs_fops);
- if (!l2cap_debugfs)
- BT_ERR("Failed to create L2CAP debug file");
- }
+ if (IS_ERR_OR_NULL(bt_debugfs))
+ return 0;
+
+ l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
+ NULL, &l2cap_debugfs_fops);
return 0;
}