summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/atm/lec.c2
-rw-r--r--net/batman-adv/fragmentation.c2
-rw-r--r--net/bluetooth/6lowpan.c90
-rw-r--r--net/bluetooth/amp.c13
-rw-r--r--net/bluetooth/hci_conn.c57
-rw-r--r--net/bluetooth/hci_core.c104
-rw-r--r--net/bluetooth/hci_event.c48
-rw-r--r--net/bluetooth/hidp/core.c10
-rw-r--r--net/bluetooth/l2cap_core.c342
-rw-r--r--net/bluetooth/l2cap_sock.c23
-rw-r--r--net/bluetooth/mgmt.c161
-rw-r--r--net/bluetooth/smp.c873
-rw-r--r--net/bluetooth/smp.h12
-rw-r--r--net/ieee802154/6lowpan_rtnl.c12
-rw-r--r--net/ieee802154/reassembly.c15
-rw-r--r--net/ipv6/ip6_fib.c2
-rw-r--r--net/mac80211/chan.c4
-rw-r--r--net/mac80211/debugfs_sta.c2
-rw-r--r--net/mac80211/mesh_plink.c7
-rw-r--r--net/mac80211/mlme.c3
-rw-r--r--net/mac80211/sta_info.c7
-rw-r--r--net/mac802154/rx.c5
-rw-r--r--net/mac802154/tx.c15
-rw-r--r--net/mac802154/wpan.c16
-rw-r--r--net/openvswitch/actions.c5
-rw-r--r--net/packet/af_packet.c17
-rw-r--r--net/packet/internal.h1
-rw-r--r--net/sched/sch_cbq.c48
-rw-r--r--net/sctp/associola.c12
-rw-r--r--net/tipc/port.h5
-rw-r--r--net/tipc/socket.c2
-rw-r--r--net/wireless/core.c7
32 files changed, 1154 insertions, 768 deletions
diff --git a/net/atm/lec.c b/net/atm/lec.c
index e4853b5..4b98f89 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -410,9 +410,11 @@ static int lec_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
priv->lane2_ops = NULL;
if (priv->lane_version > 1)
priv->lane2_ops = &lane2_ops;
+ rtnl_lock();
if (dev_set_mtu(dev, mesg->content.config.mtu))
pr_info("%s: change_mtu to %d failed\n",
dev->name, mesg->content.config.mtu);
+ rtnl_unlock();
priv->is_proxy = mesg->content.config.is_proxy;
break;
case l_flush_tran_id:
diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
index 52c43f9..fc1835c 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -188,7 +188,7 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node,
/* Reached the end of the list, so insert after 'frag_entry_last'. */
if (likely(frag_entry_last)) {
- hlist_add_behind(&frag_entry_last->list, &frag_entry_new->list);
+ hlist_add_behind(&frag_entry_new->list, &frag_entry_last->list);
chain->size += skb->len - hdr_size;
chain->timestamp = jiffies;
ret = true;
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 206b65c..0920cb6 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -39,6 +39,7 @@ static struct dentry *lowpan_control_debugfs;
struct skb_cb {
struct in6_addr addr;
+ struct in6_addr gw;
struct l2cap_chan *chan;
int status;
};
@@ -158,6 +159,54 @@ static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev,
return NULL;
}
+static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
+ struct in6_addr *daddr,
+ struct sk_buff *skb)
+{
+ struct lowpan_peer *peer, *tmp;
+ struct in6_addr *nexthop;
+ struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
+ int count = atomic_read(&dev->peer_count);
+
+ BT_DBG("peers %d addr %pI6c rt %p", count, daddr, rt);
+
+ /* If we have multiple 6lowpan peers, then check where we should
+ * send the packet. If only one peer exists, then we can send the
+ * packet right away.
+ */
+ if (count == 1)
+ return list_first_entry(&dev->peers, struct lowpan_peer,
+ list);
+
+ if (!rt) {
+ nexthop = &lowpan_cb(skb)->gw;
+
+ if (ipv6_addr_any(nexthop))
+ return NULL;
+ } else {
+ nexthop = rt6_nexthop(rt);
+
+ /* We need to remember the address because it is needed
+ * by bt_xmit() when sending the packet. In bt_xmit(), the
+ * destination routing info is not set.
+ */
+ memcpy(&lowpan_cb(skb)->gw, nexthop, sizeof(struct in6_addr));
+ }
+
+ BT_DBG("gw %pI6c", nexthop);
+
+ list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ BT_DBG("dst addr %pMR dst type %d ip %pI6c",
+ &peer->chan->dst, peer->chan->dst_type,
+ &peer->peer_addr);
+
+ if (!ipv6_addr_cmp(&peer->peer_addr, nexthop))
+ return peer;
+ }
+
+ return NULL;
+}
+
static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
{
struct lowpan_dev *entry, *tmp;
@@ -415,8 +464,18 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev,
read_unlock_irqrestore(&devices_lock, flags);
if (!peer) {
- BT_DBG("no such peer %pMR found", &addr);
- return -ENOENT;
+ /* The packet might be sent to 6lowpan interface
+ * because of routing (either via default route
+ * or user set route) so get peer according to
+ * the destination address.
+ */
+ read_lock_irqsave(&devices_lock, flags);
+ peer = peer_lookup_dst(dev, &hdr->daddr, skb);
+ read_unlock_irqrestore(&devices_lock, flags);
+ if (!peer) {
+ BT_DBG("no such peer %pMR found", &addr);
+ return -ENOENT;
+ }
}
daddr = peer->eui64_addr;
@@ -520,6 +579,8 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_ba(dev, &addr, addr_type);
+ if (!peer)
+ peer = peer_lookup_dst(dev, &lowpan_cb(skb)->addr, skb);
read_unlock_irqrestore(&devices_lock, flags);
BT_DBG("xmit %s to %pMR type %d IP %pI6c peer %p",
@@ -671,6 +732,14 @@ static struct l2cap_chan *chan_open(struct l2cap_chan *pchan)
return chan;
}
+static void set_ip_addr_bits(u8 addr_type, u8 *addr)
+{
+ if (addr_type == BDADDR_LE_PUBLIC)
+ *addr |= 0x02;
+ else
+ *addr &= ~0x02;
+}
+
static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
struct lowpan_dev *dev)
{
@@ -693,6 +762,11 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
EUI64_ADDR_LEN);
+ /* IPv6 address needs to have the U/L bit set properly so toggle
+ * it back here.
+ */
+ set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8);
+
write_lock_irqsave(&devices_lock, flags);
INIT_LIST_HEAD(&peer->list);
peer_add(dev, peer);
@@ -772,16 +846,16 @@ static inline void chan_ready_cb(struct l2cap_chan *chan)
ifup(dev->netdev);
}
-static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *chan)
+static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
{
- struct l2cap_chan *pchan;
+ struct l2cap_chan *chan;
- pchan = chan_open(chan);
- pchan->ops = chan->ops;
+ chan = chan_open(pchan);
+ chan->ops = pchan->ops;
BT_DBG("chan %p pchan %p", chan, pchan);
- return pchan;
+ return chan;
}
static void delete_netdev(struct work_struct *work)
@@ -890,7 +964,7 @@ static void chan_resume_cb(struct l2cap_chan *chan)
static long chan_get_sndtimeo_cb(struct l2cap_chan *chan)
{
- return msecs_to_jiffies(1000);
+ return L2CAP_CONN_TIMEOUT;
}
static const struct l2cap_ops bt_6lowpan_chan_ops = {
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index 016cdb6..2640d78 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -149,15 +149,14 @@ static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
if (ret) {
BT_DBG("crypto_ahash_setkey failed: err %d", ret);
} else {
- struct {
- struct shash_desc shash;
- char ctx[crypto_shash_descsize(tfm)];
- } desc;
+ char desc[sizeof(struct shash_desc) +
+ crypto_shash_descsize(tfm)] CRYPTO_MINALIGN_ATTR;
+ struct shash_desc *shash = (struct shash_desc *)desc;
- desc.shash.tfm = tfm;
- desc.shash.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ shash->tfm = tfm;
+ shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
- ret = crypto_shash_digest(&desc.shash, plaintext, psize,
+ ret = crypto_shash_digest(shash, plaintext, psize,
output);
}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b50dabb..e3d7ae9 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -122,17 +122,30 @@ static void hci_reject_sco(struct hci_conn *conn)
hci_send_cmd(conn->hdev, HCI_OP_REJECT_SYNC_CONN_REQ, sizeof(cp), &cp);
}
-void hci_disconnect(struct hci_conn *conn, __u8 reason)
+int hci_disconnect(struct hci_conn *conn, __u8 reason)
{
struct hci_cp_disconnect cp;
BT_DBG("hcon %p", conn);
+ /* When we are master of an established connection and it enters
+ * the disconnect timeout, then go ahead and try to read the
+ * current clock offset. Processing of the result is done
+ * within the event handling and hci_clock_offset_evt function.
+ */
+ if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) {
+ struct hci_dev *hdev = conn->hdev;
+ struct hci_cp_read_clock_offset cp;
+
+ cp.handle = cpu_to_le16(conn->handle);
+ hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(cp), &cp);
+ }
+
conn->state = BT_DISCONN;
cp.handle = cpu_to_le16(conn->handle);
cp.reason = reason;
- hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
+ return hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
}
static void hci_amp_disconn(struct hci_conn *conn)
@@ -325,25 +338,6 @@ static void hci_conn_timeout(struct work_struct *work)
hci_amp_disconn(conn);
} else {
__u8 reason = hci_proto_disconn_ind(conn);
-
- /* When we are master of an established connection
- * and it enters the disconnect timeout, then go
- * ahead and try to read the current clock offset.
- *
- * Processing of the result is done within the
- * event handling and hci_clock_offset_evt function.
- */
- if (conn->type == ACL_LINK &&
- conn->role == HCI_ROLE_MASTER) {
- struct hci_dev *hdev = conn->hdev;
- struct hci_cp_read_clock_offset cp;
-
- cp.handle = cpu_to_le16(conn->handle);
-
- hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET,
- sizeof(cp), &cp);
- }
-
hci_disconnect(conn, reason);
}
break;
@@ -589,6 +583,15 @@ EXPORT_SYMBOL(hci_get_route);
void hci_le_conn_failed(struct hci_conn *conn, u8 status)
{
struct hci_dev *hdev = conn->hdev;
+ struct hci_conn_params *params;
+
+ params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
+ conn->dst_type);
+ if (params && params->conn) {
+ hci_conn_drop(params->conn);
+ hci_conn_put(params->conn);
+ params->conn = NULL;
+ }
conn->state = BT_CLOSED;
@@ -1282,11 +1285,16 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
BT_DBG("%s hcon %p", hdev->name, conn);
+ if (test_bit(HCI_CONN_DROP, &conn->flags)) {
+ BT_DBG("Refusing to create new hci_chan");
+ return NULL;
+ }
+
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
if (!chan)
return NULL;
- chan->conn = conn;
+ chan->conn = hci_conn_get(conn);
skb_queue_head_init(&chan->data_q);
chan->state = BT_CONNECTED;
@@ -1306,7 +1314,10 @@ void hci_chan_del(struct hci_chan *chan)
synchronize_rcu();
- hci_conn_drop(conn);
+ /* Prevent new hci_chan's to be created for this hci_conn */
+ set_bit(HCI_CONN_DROP, &conn->flags);
+
+ hci_conn_put(conn);
skb_queue_purge(&chan->data_q);
kfree(chan);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index c32d361..0d3782a 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1898,6 +1898,8 @@ static int __hci_init(struct hci_dev *hdev)
debugfs_create_u16("discov_interleaved_timeout", 0644,
hdev->debugfs,
&hdev->discov_interleaved_timeout);
+
+ smp_register(hdev);
}
return 0;
@@ -2536,8 +2538,14 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev)
{
struct hci_conn_params *p;
- list_for_each_entry(p, &hdev->le_conn_params, list)
+ list_for_each_entry(p, &hdev->le_conn_params, list) {
+ if (p->conn) {
+ hci_conn_drop(p->conn);
+ hci_conn_put(p->conn);
+ p->conn = NULL;
+ }
list_del_init(&p->action);
+ }
BT_DBG("All LE pending actions cleared");
}
@@ -2578,8 +2586,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
hci_dev_lock(hdev);
hci_inquiry_cache_flush(hdev);
- hci_conn_hash_flush(hdev);
hci_pend_le_actions_clear(hdev);
+ hci_conn_hash_flush(hdev);
hci_dev_unlock(hdev);
hci_notify(hdev, HCI_DEV_DOWN);
@@ -3233,7 +3241,7 @@ struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
}
list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
- if (smp_irk_matches(hdev->tfm_aes, irk->val, rpa)) {
+ if (smp_irk_matches(hdev, irk->val, rpa)) {
bacpy(&irk->rpa, rpa);
return irk;
}
@@ -3718,6 +3726,18 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
return 0;
}
+static void hci_conn_params_free(struct hci_conn_params *params)
+{
+ if (params->conn) {
+ hci_conn_drop(params->conn);
+ hci_conn_put(params->conn);
+ }
+
+ list_del(&params->action);
+ list_del(&params->list);
+ kfree(params);
+}
+
/* This function requires the caller holds hdev->lock */
void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
{
@@ -3727,9 +3747,7 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
if (!params)
return;
- list_del(&params->action);
- list_del(&params->list);
- kfree(params);
+ hci_conn_params_free(params);
hci_update_background_scan(hdev);
@@ -3756,11 +3774,8 @@ void hci_conn_params_clear_all(struct hci_dev *hdev)
{
struct hci_conn_params *params, *tmp;
- list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) {
- list_del(&params->action);
- list_del(&params->list);
- kfree(params);
- }
+ list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list)
+ hci_conn_params_free(params);
hci_update_background_scan(hdev);
@@ -3882,7 +3897,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
!bacmp(&hdev->random_addr, &hdev->rpa))
return 0;
- err = smp_generate_rpa(hdev->tfm_aes, hdev->irk, &hdev->rpa);
+ err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa);
if (err < 0) {
BT_ERR("%s failed to generate new RPA", hdev->name);
return err;
@@ -4090,18 +4105,9 @@ int hci_register_dev(struct hci_dev *hdev)
dev_set_name(&hdev->dev, "%s", hdev->name);
- hdev->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0,
- CRYPTO_ALG_ASYNC);
- if (IS_ERR(hdev->tfm_aes)) {
- BT_ERR("Unable to create crypto context");
- error = PTR_ERR(hdev->tfm_aes);
- hdev->tfm_aes = NULL;
- goto err_wqueue;
- }
-
error = device_add(&hdev->dev);
if (error < 0)
- goto err_tfm;
+ goto err_wqueue;
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops,
@@ -4143,8 +4149,6 @@ int hci_register_dev(struct hci_dev *hdev)
return id;
-err_tfm:
- crypto_free_blkcipher(hdev->tfm_aes);
err_wqueue:
destroy_workqueue(hdev->workqueue);
destroy_workqueue(hdev->req_workqueue);
@@ -4196,8 +4200,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
rfkill_destroy(hdev->rfkill);
}
- if (hdev->tfm_aes)
- crypto_free_blkcipher(hdev->tfm_aes);
+ smp_unregister(hdev);
device_del(&hdev->dev);
@@ -5680,3 +5683,52 @@ void hci_update_background_scan(struct hci_dev *hdev)
if (err)
BT_ERR("Failed to run HCI request: err %d", err);
}
+
+static bool disconnected_whitelist_entries(struct hci_dev *hdev)
+{
+ struct bdaddr_list *b;
+
+ list_for_each_entry(b, &hdev->whitelist, list) {
+ struct hci_conn *conn;
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &b->bdaddr);
+ if (!conn)
+ return true;
+
+ if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
+ return true;
+ }
+
+ return false;
+}
+
+void hci_update_page_scan(struct hci_dev *hdev, struct hci_request *req)
+{
+ u8 scan;
+
+ if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
+ return;
+
+ if (!hdev_is_powered(hdev))
+ return;
+
+ if (mgmt_powering_down(hdev))
+ return;
+
+ if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) ||
+ disconnected_whitelist_entries(hdev))
+ scan = SCAN_PAGE;
+ else
+ scan = SCAN_DISABLED;
+
+ if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE))
+ return;
+
+ if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+ scan |= SCAN_INQUIRY;
+
+ if (req)
+ hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ else
+ hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index be35598..3a8381a 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2071,6 +2071,8 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
cp.handle = ev->handle;
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
sizeof(cp), &cp);
+
+ hci_update_page_scan(hdev, NULL);
}
/* Set packet type for incoming connection */
@@ -2247,9 +2249,12 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type,
reason, mgmt_connected);
- if (conn->type == ACL_LINK &&
- test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
- hci_remove_link_key(hdev, &conn->dst);
+ if (conn->type == ACL_LINK) {
+ if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
+ hci_remove_link_key(hdev, &conn->dst);
+
+ hci_update_page_scan(hdev, NULL);
+ }
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (params) {
@@ -2315,8 +2320,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->sec_level = conn->pending_sec_level;
}
} else {
- mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
- ev->status);
+ mgmt_auth_failed(conn, ev->status);
}
clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
@@ -3895,8 +3899,7 @@ static void hci_simple_pair_complete_evt(struct hci_dev *hdev,
* event gets always produced as initiator and is also mapped to
* the mgmt_auth_failed event */
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status)
- mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
- ev->status);
+ mgmt_auth_failed(conn, ev->status);
hci_conn_drop(conn);
@@ -4188,16 +4191,16 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn->dst_type = irk->addr_type;
}
- if (conn->dst_type == ADDR_LE_DEV_PUBLIC)
- addr_type = BDADDR_LE_PUBLIC;
- else
- addr_type = BDADDR_LE_RANDOM;
-
if (ev->status) {
hci_le_conn_failed(conn, ev->status);
goto unlock;
}
+ if (conn->dst_type == ADDR_LE_DEV_PUBLIC)
+ addr_type = BDADDR_LE_PUBLIC;
+ else
+ addr_type = BDADDR_LE_RANDOM;
+
/* Drop the connection if the device is blocked */
if (hci_bdaddr_list_lookup(&hdev->blacklist, &conn->dst, addr_type)) {
hci_conn_drop(conn);
@@ -4220,9 +4223,16 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_proto_connect_cfm(conn, ev->status);
- params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
- if (params)
+ params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
+ conn->dst_type);
+ if (params) {
list_del_init(&params->action);
+ if (params->conn) {
+ hci_conn_drop(params->conn);
+ hci_conn_put(params->conn);
+ params->conn = NULL;
+ }
+ }
unlock:
hci_update_background_scan(hdev);
@@ -4304,8 +4314,16 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
HCI_LE_AUTOCONN_TIMEOUT, HCI_ROLE_MASTER);
- if (!IS_ERR(conn))
+ if (!IS_ERR(conn)) {
+ /* Store the pointer since we don't really have any
+ * other owner of the object besides the params that
+ * triggered it. This way we can abort the connection if
+ * the parameters get removed and keep the reference
+ * count consistent once the connection is established.
+ */
+ params->conn = hci_conn_get(conn);
return;
+ }
switch (PTR_ERR(conn)) {
case -EBUSY:
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 6c7ecf1..1b7d605 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -915,7 +915,7 @@ static int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr,
/* connection management */
bacpy(&session->bdaddr, bdaddr);
- session->conn = conn;
+ session->conn = l2cap_conn_get(conn);
session->user.probe = hidp_session_probe;
session->user.remove = hidp_session_remove;
session->ctrl_sock = ctrl_sock;
@@ -941,13 +941,13 @@ static int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr,
if (ret)
goto err_free;
- l2cap_conn_get(session->conn);
get_file(session->intr_sock->file);
get_file(session->ctrl_sock->file);
*out = session;
return 0;
err_free:
+ l2cap_conn_put(session->conn);
kfree(session);
return ret;
}
@@ -1327,10 +1327,8 @@ int hidp_connection_add(struct hidp_connadd_req *req,
conn = NULL;
l2cap_chan_lock(chan);
- if (chan->conn) {
- l2cap_conn_get(chan->conn);
- conn = chan->conn;
- }
+ if (chan->conn)
+ conn = l2cap_conn_get(chan->conn);
l2cap_chan_unlock(chan);
if (!conn)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 46547b9..b71430c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -210,6 +210,10 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
{
write_lock(&chan_list_lock);
+ /* Override the defaults (which are for conn-oriented) */
+ chan->omtu = L2CAP_DEFAULT_MTU;
+ chan->chan_type = L2CAP_CHAN_FIXED;
+
chan->scid = scid;
write_unlock(&chan_list_lock);
@@ -542,7 +546,10 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
l2cap_chan_hold(chan);
- hci_conn_hold(conn->hcon);
+ /* Only keep a reference for fixed channels if they requested it */
+ if (chan->chan_type != L2CAP_CHAN_FIXED ||
+ test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
+ hci_conn_hold(conn->hcon);
list_add(&chan->list, &conn->chan_l);
}
@@ -562,6 +569,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
+ chan->ops->teardown(chan, err);
+
if (conn) {
struct amp_mgr *mgr = conn->hcon->amp_mgr;
/* Delete from channel list */
@@ -571,7 +580,12 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
chan->conn = NULL;
- if (chan->scid != L2CAP_CID_A2MP)
+ /* Reference was only held for non-fixed channels or
+ * fixed channels that explicitly requested it using the
+ * FLAG_HOLD_HCI_CONN flag.
+ */
+ if (chan->chan_type != L2CAP_CHAN_FIXED ||
+ test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
hci_conn_drop(conn->hcon);
if (mgr && mgr->bredr_chan == chan)
@@ -585,8 +599,6 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
amp_disconnect_logical_link(hs_hchan);
}
- chan->ops->teardown(chan, err);
-
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
return;
@@ -619,9 +631,11 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
}
EXPORT_SYMBOL_GPL(l2cap_chan_del);
-void l2cap_conn_update_id_addr(struct hci_conn *hcon)
+static void l2cap_conn_update_id_addr(struct work_struct *work)
{
- struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
+ id_addr_update_work);
+ struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan;
mutex_lock(&conn->chan_lock);
@@ -1082,6 +1096,9 @@ static void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, bool poll)
static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
{
+ if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED)
+ return true;
+
return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
}
@@ -1417,71 +1434,18 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
mutex_unlock(&conn->chan_lock);
}
-/* Find socket with cid and source/destination bdaddr.
- * Returns closest match, locked.
- */
-static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
- bdaddr_t *src,
- bdaddr_t *dst)
-{
- struct l2cap_chan *c, *c1 = NULL;
-
- read_lock(&chan_list_lock);
-
- list_for_each_entry(c, &chan_list, global_l) {
- if (state && c->state != state)
- continue;
-
- if (c->scid == cid) {
- int src_match, dst_match;
- int src_any, dst_any;
-
- /* Exact match. */
- 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(&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;
- }
- }
-
- read_unlock(&chan_list_lock);
-
- return c1;
-}
-
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
{
struct hci_conn *hcon = conn->hcon;
struct hci_dev *hdev = hcon->hdev;
- struct l2cap_chan *chan, *pchan;
- u8 dst_type;
- BT_DBG("");
+ BT_DBG("%s conn %p", hdev->name, conn);
- /* Check if we have socket listening on cid */
- pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
- &hcon->src, &hcon->dst);
- if (!pchan)
- return;
-
- /* Client ATT sockets should override the server one */
- if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
- return;
-
- dst_type = bdaddr_type(hcon, hcon->dst_type);
-
- /* If device is blocked, do not create a channel for it */
- if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type))
- return;
+ /* For outgoing pairing which doesn't necessarily have an
+ * associated socket (e.g. mgmt_pair_device).
+ */
+ if (hcon->out)
+ smp_conn_security(hcon, hcon->pending_sec_level);
/* For LE slave connections, make sure the connection interval
* is in the range of the minium and maximum interval that has
@@ -1501,22 +1465,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
l2cap_send_cmd(conn, l2cap_get_ident(conn),
L2CAP_CONN_PARAM_UPDATE_REQ, sizeof(req), &req);
}
-
- l2cap_chan_lock(pchan);
-
- chan = pchan->ops->new_connection(pchan);
- if (!chan)
- goto clean;
-
- 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:
- l2cap_chan_unlock(pchan);
}
static void l2cap_conn_ready(struct l2cap_conn *conn)
@@ -1526,17 +1474,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
BT_DBG("conn %p", conn);
- /* For outgoing pairing which doesn't necessarily have an
- * associated socket (e.g. mgmt_pair_device).
- */
- if (hcon->out && hcon->type == LE_LINK)
- smp_conn_security(hcon, hcon->pending_sec_level);
-
mutex_lock(&conn->chan_lock);
- if (hcon->type == LE_LINK)
- l2cap_le_conn_ready(conn);
-
list_for_each_entry(chan, &conn->chan_l, list) {
l2cap_chan_lock(chan);
@@ -1560,6 +1499,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
mutex_unlock(&conn->chan_lock);
+ if (hcon->type == LE_LINK)
+ l2cap_le_conn_ready(conn);
+
queue_work(hcon->hdev->workqueue, &conn->pending_rx_work);
}
@@ -1695,8 +1637,14 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
if (work_pending(&conn->pending_rx_work))
cancel_work_sync(&conn->pending_rx_work);
+ if (work_pending(&conn->id_addr_update_work))
+ cancel_work_sync(&conn->id_addr_update_work);
+
l2cap_unregister_all_users(conn);
+ /* Force the connection to be immediately dropped */
+ hcon->disc_timeout = 0;
+
mutex_lock(&conn->chan_lock);
/* Kill channels */
@@ -1719,29 +1667,11 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
cancel_delayed_work_sync(&conn->info_timer);
- if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) {
- cancel_delayed_work_sync(&conn->security_timer);
- smp_chan_destroy(conn);
- }
-
hcon->l2cap_data = NULL;
conn->hchan = NULL;
l2cap_conn_put(conn);
}
-static void security_timeout(struct work_struct *work)
-{
- struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
- security_timer.work);
-
- BT_DBG("conn %p", conn);
-
- if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) {
- smp_chan_destroy(conn);
- l2cap_conn_del(conn->hcon, ETIMEDOUT);
- }
-}
-
static void l2cap_conn_free(struct kref *ref)
{
struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
@@ -1750,9 +1680,10 @@ static void l2cap_conn_free(struct kref *ref)
kfree(conn);
}
-void l2cap_conn_get(struct l2cap_conn *conn)
+struct l2cap_conn *l2cap_conn_get(struct l2cap_conn *conn)
{
kref_get(&conn->ref);
+ return conn;
}
EXPORT_SYMBOL(l2cap_conn_get);
@@ -1794,6 +1725,7 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
src_match = !bacmp(&c->src, src);
dst_match = !bacmp(&c->dst, dst);
if (src_match && dst_match) {
+ l2cap_chan_hold(c);
read_unlock(&chan_list_lock);
return c;
}
@@ -1807,6 +1739,9 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
}
}
+ if (c1)
+ l2cap_chan_hold(c1);
+
read_unlock(&chan_list_lock);
return c1;
@@ -2027,10 +1962,12 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
tx_skb->data + L2CAP_HDR_SIZE);
}
+ /* Update FCS */
if (chan->fcs == L2CAP_FCS_CRC16) {
- u16 fcs = crc16(0, (u8 *) tx_skb->data, tx_skb->len);
- put_unaligned_le16(fcs, skb_put(tx_skb,
- L2CAP_FCS_SIZE));
+ u16 fcs = crc16(0, (u8 *) tx_skb->data,
+ tx_skb->len - L2CAP_FCS_SIZE);
+ put_unaligned_le16(fcs, skb_tail_pointer(tx_skb) -
+ L2CAP_FCS_SIZE);
}
l2cap_do_send(chan, tx_skb);
@@ -2334,7 +2271,6 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
} else {
sar = L2CAP_SAR_START;
sdu_len = len;
- pdu_len -= L2CAP_SDULEN_SIZE;
}
while (len > 0) {
@@ -2349,10 +2285,8 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
__skb_queue_tail(seg_queue, skb);
len -= pdu_len;
- if (sdu_len) {
+ if (sdu_len)
sdu_len = 0;
- pdu_len += L2CAP_SDULEN_SIZE;
- }
if (len <= pdu_len) {
sar = L2CAP_SAR_END;
@@ -2418,12 +2352,8 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
BT_DBG("chan %p, msg %p, len %zu", chan, msg, len);
- pdu_len = chan->conn->mtu - L2CAP_HDR_SIZE;
-
- pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
-
sdu_len = len;
- pdu_len -= L2CAP_SDULEN_SIZE;
+ pdu_len = chan->remote_mps - L2CAP_SDULEN_SIZE;
while (len > 0) {
if (len <= pdu_len)
@@ -3884,6 +3814,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
response:
l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
+ l2cap_chan_put(pchan);
sendresp:
rsp.scid = cpu_to_le16(scid);
@@ -5487,6 +5418,11 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
l2cap_state_change(chan, BT_CONNECT2);
+ /* The following result value is actually not defined
+ * for LE CoC but we use it to let the function know
+ * that it should bail out after doing its cleanup
+ * instead of sending a response.
+ */
result = L2CAP_CR_PEND;
chan->ops->defer(chan);
} else {
@@ -5497,6 +5433,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
response_unlock:
l2cap_chan_unlock(pchan);
mutex_unlock(&conn->chan_lock);
+ l2cap_chan_put(pchan);
if (result == L2CAP_CR_PEND)
return 0;
@@ -6845,12 +6782,12 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
struct l2cap_chan *chan;
if (hcon->type != ACL_LINK)
- goto drop;
+ goto free_skb;
chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst,
ACL_LINK);
if (!chan)
- goto drop;
+ goto free_skb;
BT_DBG("chan %p, len %d", chan, skb->len);
@@ -6864,36 +6801,14 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
bt_cb(skb)->psm = psm;
- if (!chan->ops->recv(chan, skb))
- return;
-
-drop:
- kfree_skb(skb);
-}
-
-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,
- &hcon->src, &hcon->dst);
- if (!chan)
- goto drop;
-
- BT_DBG("chan %p, len %d", chan, skb->len);
-
- if (chan->imtu < skb->len)
- goto drop;
-
- if (!chan->ops->recv(chan, skb))
+ if (!chan->ops->recv(chan, skb)) {
+ l2cap_chan_put(chan);
return;
+ }
drop:
+ l2cap_chan_put(chan);
+free_skb:
kfree_skb(skb);
}
@@ -6942,19 +6857,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
l2cap_conless_channel(conn, psm, skb);
break;
- case L2CAP_CID_ATT:
- 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);
- break;
-
default:
l2cap_data_channel(conn, cid, skb);
break;
@@ -6993,8 +6899,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
kref_init(&conn->ref);
hcon->l2cap_data = conn;
- conn->hcon = hcon;
- hci_conn_get(conn->hcon);
+ conn->hcon = hci_conn_get(hcon);
conn->hchan = hchan;
BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
@@ -7023,13 +6928,11 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
INIT_LIST_HEAD(&conn->chan_l);
INIT_LIST_HEAD(&conn->users);
- if (hcon->type == LE_LINK)
- INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
- else
- INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
+ INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
skb_queue_head_init(&conn->pending_rx);
INIT_WORK(&conn->pending_rx_work, process_pending_rx);
+ INIT_WORK(&conn->id_addr_update_work, l2cap_conn_update_id_addr);
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
@@ -7172,9 +7075,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bacpy(&chan->src, &hcon->src);
chan->src_type = bdaddr_type(hcon, hcon->src_type);
- l2cap_chan_unlock(chan);
l2cap_chan_add(conn, chan);
- l2cap_chan_lock(chan);
/* l2cap_chan_add takes its own ref so we can drop this one */
hci_conn_drop(hcon);
@@ -7239,19 +7140,99 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
return exact ? lm1 : lm2;
}
+/* Find the next fixed channel in BT_LISTEN state, continue iteration
+ * from an existing channel in the list or from the beginning of the
+ * global list (by passing NULL as first parameter).
+ */
+static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
+ bdaddr_t *src, u8 link_type)
+{
+ read_lock(&chan_list_lock);
+
+ if (c)
+ c = list_next_entry(c, global_l);
+ else
+ c = list_entry(chan_list.next, typeof(*c), global_l);
+
+ list_for_each_entry_from(c, &chan_list, global_l) {
+ if (c->chan_type != L2CAP_CHAN_FIXED)
+ continue;
+ if (c->state != BT_LISTEN)
+ continue;
+ if (bacmp(&c->src, src) && bacmp(&c->src, BDADDR_ANY))
+ continue;
+ if (link_type == ACL_LINK && c->src_type != BDADDR_BREDR)
+ continue;
+ if (link_type == LE_LINK && c->src_type == BDADDR_BREDR)
+ continue;
+
+ l2cap_chan_hold(c);
+ read_unlock(&chan_list_lock);
+ return c;
+ }
+
+ read_unlock(&chan_list_lock);
+
+ return NULL;
+}
+
void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
{
+ struct hci_dev *hdev = hcon->hdev;
struct l2cap_conn *conn;
+ struct l2cap_chan *pchan;
+ u8 dst_type;
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
- if (!status) {
- conn = l2cap_conn_add(hcon);
- if (conn)
- l2cap_conn_ready(conn);
- } else {
+ if (status) {
l2cap_conn_del(hcon, bt_to_errno(status));
+ return;
+ }
+
+ conn = l2cap_conn_add(hcon);
+ if (!conn)
+ return;
+
+ dst_type = bdaddr_type(hcon, hcon->dst_type);
+
+ /* If device is blocked, do not create channels for it */
+ if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type))
+ return;
+
+ /* Find fixed channels and notify them of the new connection. We
+ * use multiple individual lookups, continuing each time where
+ * we left off, because the list lock would prevent calling the
+ * potentially sleeping l2cap_chan_lock() function.
+ */
+ pchan = l2cap_global_fixed_chan(NULL, &hdev->bdaddr, hcon->type);
+ while (pchan) {
+ struct l2cap_chan *chan, *next;
+
+ /* Client fixed channels should override server ones */
+ if (__l2cap_get_chan_by_dcid(conn, pchan->scid))
+ goto next;
+
+ l2cap_chan_lock(pchan);
+ chan = pchan->ops->new_connection(pchan);
+ if (chan) {
+ 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);
+ }
+
+ l2cap_chan_unlock(pchan);
+next:
+ next = l2cap_global_fixed_chan(pchan, &hdev->bdaddr,
+ hcon->type);
+ l2cap_chan_put(pchan);
+ pchan = next;
}
+
+ l2cap_conn_ready(conn);
}
int l2cap_disconn_ind(struct hci_conn *hcon)
@@ -7299,12 +7280,6 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
BT_DBG("conn %p status 0x%2.2x encrypt %u", conn, status, encrypt);
- if (hcon->type == LE_LINK) {
- if (!status && encrypt)
- smp_distribute_keys(conn);
- cancel_delayed_work(&conn->security_timer);
- }
-
mutex_lock(&conn->chan_lock);
list_for_each_entry(chan, &conn->chan_l, list) {
@@ -7318,15 +7293,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
continue;
}
- if (chan->scid == L2CAP_CID_ATT) {
- if (!status && encrypt) {
- chan->sec_level = hcon->sec_level;
- l2cap_chan_ready(chan);
- }
-
- l2cap_chan_unlock(chan);
- continue;
- }
+ if (!status && encrypt)
+ chan->sec_level = hcon->sec_level;
if (!__l2cap_no_conn_pending(chan)) {
l2cap_chan_unlock(chan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 1884f72..31f106e 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -99,15 +99,6 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
return -EINVAL;
- if (la.l2_cid) {
- /* When the socket gets created it defaults to
- * CHAN_CONN_ORIENTED, so we need to overwrite the
- * default here.
- */
- chan->chan_type = L2CAP_CHAN_FIXED;
- chan->omtu = L2CAP_DEFAULT_MTU;
- }
-
if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
/* We only allow ATT user space socket */
if (la.l2_cid &&
@@ -155,6 +146,14 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
case L2CAP_CHAN_RAW:
chan->sec_level = BT_SECURITY_SDP;
break;
+ case L2CAP_CHAN_FIXED:
+ /* Fixed channels default to the L2CAP core not holding a
+ * hci_conn reference for them. For fixed channels mapping to
+ * L2CAP sockets we do want to hold a reference so set the
+ * appropriate flag to request it.
+ */
+ set_bit(FLAG_HOLD_HCI_CONN, &chan->flags);
+ break;
}
bacpy(&chan->src, &la.l2_bdaddr);
@@ -790,6 +789,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
if (chan->scid == L2CAP_CID_ATT) {
if (smp_conn_security(conn->hcon, sec.level))
break;
+ set_bit(FLAG_PENDING_SECURITY, &chan->flags);
sk->sk_state = BT_CONFIG;
chan->state = BT_CONFIG;
@@ -1359,6 +1359,11 @@ static void l2cap_sock_resume_cb(struct l2cap_chan *chan)
{
struct sock *sk = chan->data;
+ if (test_and_clear_bit(FLAG_PENDING_SECURITY, &chan->flags)) {
+ sk->sk_state = BT_CONNECTED;
+ chan->state = BT_CONNECTED;
+ }
+
clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
sk->sk_state_change(sk);
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index b8554d4..efb71b0 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -129,9 +129,6 @@ static const u16 mgmt_events[] = {
#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
-#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
- !test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
-
struct pending_cmd {
struct list_head list;
u16 opcode;
@@ -1536,9 +1533,11 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
/* When the discoverable mode gets changed, make sure
* that class of device has the limited discoverable
- * bit correctly set.
+ * bit correctly set. Also update page scan based on whitelist
+ * entries.
*/
hci_req_init(&req, hdev);
+ hci_update_page_scan(hdev, &req);
update_class(&req);
hci_req_run(&req, NULL);
@@ -1785,6 +1784,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
if (conn_changed || discov_changed) {
new_settings(hdev, cmd->sk);
+ hci_update_page_scan(hdev, NULL);
if (discov_changed)
mgmt_update_adv_data(hdev);
hci_update_background_scan(hdev);
@@ -1818,6 +1818,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
return err;
if (changed) {
+ hci_update_page_scan(hdev, NULL);
hci_update_background_scan(hdev);
return new_settings(hdev, sk);
}
@@ -2787,7 +2788,6 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
{
struct mgmt_cp_disconnect *cp = data;
struct mgmt_rp_disconnect rp;
- struct hci_cp_disconnect dc;
struct pending_cmd *cmd;
struct hci_conn *conn;
int err;
@@ -2835,10 +2835,7 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
- dc.handle = cpu_to_le16(conn->handle);
- dc.reason = HCI_ERROR_REMOTE_USER_TERM;
-
- err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
+ err = hci_disconnect(conn, HCI_ERROR_REMOTE_USER_TERM);
if (err < 0)
mgmt_pending_remove(cmd);
@@ -3062,6 +3059,7 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
conn->disconn_cfm_cb = NULL;
hci_conn_drop(conn);
+ hci_conn_put(conn);
mgmt_pending_remove(cmd);
}
@@ -3211,7 +3209,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
}
conn->io_capability = cp->io_cap;
- cmd->user_data = conn;
+ cmd->user_data = hci_conn_get(conn);
if ((conn->state == BT_CONNECTED || conn->state == BT_CONFIG) &&
hci_conn_security(conn, sec_level, auth_type, true))
@@ -4381,27 +4379,6 @@ unlock:
return err;
}
-static void set_bredr_scan(struct hci_request *req)
-{
- struct hci_dev *hdev = req->hdev;
- u8 scan = 0;
-
- /* Ensure that fast connectable is disabled. This function will
- * not do anything if the page scan parameters are already what
- * they should be.
- */
- write_fast_connectable(req, false);
-
- if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) ||
- !list_empty(&hdev->whitelist))
- scan |= SCAN_PAGE;
- if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
- scan |= SCAN_INQUIRY;
-
- if (scan)
- hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
-}
-
static void set_bredr_complete(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
@@ -4507,9 +4484,8 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
hci_req_init(&req, hdev);
- if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) ||
- !list_empty(&hdev->whitelist))
- set_bredr_scan(&req);
+ write_fast_connectable(&req, false);
+ hci_update_page_scan(hdev, &req);
/* Since only the advertising data flags will change, there
* is no need to update the scan response data.
@@ -4935,6 +4911,7 @@ static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
match->mgmt_status, &rp, sizeof(rp));
hci_conn_drop(conn);
+ hci_conn_put(conn);
mgmt_pending_remove(cmd);
}
@@ -5091,7 +5068,7 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
}
hci_conn_hold(conn);
- cmd->user_data = conn;
+ cmd->user_data = hci_conn_get(conn);
conn->conn_info_timestamp = jiffies;
} else {
@@ -5155,8 +5132,10 @@ send_rsp:
cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status),
&rp, sizeof(rp));
mgmt_pending_remove(cmd);
- if (conn)
+ if (conn) {
hci_conn_drop(conn);
+ hci_conn_put(conn);
+ }
unlock:
hci_dev_unlock(hdev);
@@ -5219,7 +5198,7 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
if (conn) {
hci_conn_hold(conn);
- cmd->user_data = conn;
+ cmd->user_data = hci_conn_get(conn);
hci_cp.handle = cpu_to_le16(conn->handle);
hci_cp.which = 0x01; /* Piconet clock */
@@ -5235,27 +5214,6 @@ unlock:
return err;
}
-/* Helper for Add/Remove Device commands */
-static void update_page_scan(struct hci_dev *hdev, u8 scan)
-{
- if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
- return;
-
- if (!hdev_is_powered(hdev))
- return;
-
- /* If HCI_CONNECTABLE is set then Add/Remove Device should not
- * make any changes to page scanning.
- */
- if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- return;
-
- if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
- scan |= SCAN_INQUIRY;
-
- hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
-}
-
static void device_added(struct sock *sk, struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 type, u8 action)
{
@@ -5291,8 +5249,6 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev);
if (cp->addr.type == BDADDR_BREDR) {
- bool update_scan;
-
/* Only incoming connections action is supported for now */
if (cp->action != 0x01) {
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
@@ -5301,15 +5257,12 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
- update_scan = list_empty(&hdev->whitelist);
-
err = hci_bdaddr_list_add(&hdev->whitelist, &cp->addr.bdaddr,
cp->addr.type);
if (err)
goto unlock;
- if (update_scan)
- update_page_scan(hdev, SCAN_PAGE);
+ hci_update_page_scan(hdev, NULL);
goto added;
}
@@ -5392,8 +5345,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
- if (list_empty(&hdev->whitelist))
- update_page_scan(hdev, SCAN_DISABLED);
+ hci_update_page_scan(hdev, NULL);
device_removed(sk, hdev, &cp->addr.bdaddr,
cp->addr.type);
@@ -5444,7 +5396,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
kfree(b);
}
- update_page_scan(hdev, SCAN_DISABLED);
+ hci_update_page_scan(hdev, NULL);
list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
@@ -5969,8 +5921,8 @@ static int powered_update_hci(struct hci_dev *hdev)
sizeof(link_sec), &link_sec);
if (lmp_bredr_capable(hdev)) {
- if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
- set_bredr_scan(&req);
+ write_fast_connectable(&req, false);
+ hci_update_page_scan(hdev, &req);
update_class(&req);
update_name(&req);
update_eir(&req);
@@ -6281,25 +6233,35 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_remove(cmd);
}
+bool mgmt_powering_down(struct hci_dev *hdev)
+{
+ struct pending_cmd *cmd;
+ struct mgmt_mode *cp;
+
+ cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
+ if (!cmd)
+ return false;
+
+ cp = cmd->param;
+ if (!cp->val)
+ return true;
+
+ return false;
+}
+
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected)
{
struct mgmt_ev_device_disconnected ev;
- struct pending_cmd *power_off;
struct sock *sk = NULL;
- power_off = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
- if (power_off) {
- struct mgmt_mode *cp = power_off->param;
-
- /* The connection is still in hci_conn_hash so test for 1
- * instead of 0 to know if this is the last one.
- */
- if (!cp->val && hci_conn_count(hdev) == 1) {
- cancel_delayed_work(&hdev->power_off);
- queue_work(hdev->req_workqueue, &hdev->power_off.work);
- }
+ /* The connection is still in hci_conn_hash so test for 1
+ * instead of 0 to know if this is the last one.
+ */
+ if (mgmt_powering_down(hdev) && hci_conn_count(hdev) == 1) {
+ cancel_delayed_work(&hdev->power_off);
+ queue_work(hdev->req_workqueue, &hdev->power_off.work);
}
if (!mgmt_connected)
@@ -6359,19 +6321,13 @@ void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 status)
{
struct mgmt_ev_connect_failed ev;
- struct pending_cmd *power_off;
-
- power_off = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev);
- if (power_off) {
- struct mgmt_mode *cp = power_off->param;
- /* The connection is still in hci_conn_hash so test for 1
- * instead of 0 to know if this is the last one.
- */
- if (!cp->val && hci_conn_count(hdev) == 1) {
- cancel_delayed_work(&hdev->power_off);
- queue_work(hdev->req_workqueue, &hdev->power_off.work);
- }
+ /* The connection is still in hci_conn_hash so test for 1
+ * instead of 0 to know if this is the last one.
+ */
+ if (mgmt_powering_down(hdev) && hci_conn_count(hdev) == 1) {
+ cancel_delayed_work(&hdev->power_off);
+ queue_work(hdev->req_workqueue, &hdev->power_off.work);
}
bacpy(&ev.addr.bdaddr, bdaddr);
@@ -6529,16 +6485,23 @@ int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL);
}
-void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u8 status)
+void mgmt_auth_failed(struct hci_conn *conn, u8 hci_status)
{
struct mgmt_ev_auth_failed ev;
+ struct pending_cmd *cmd;
+ u8 status = mgmt_status(hci_status);
- bacpy(&ev.addr.bdaddr, bdaddr);
- ev.addr.type = link_to_bdaddr(link_type, addr_type);
- ev.status = mgmt_status(status);
+ bacpy(&ev.addr.bdaddr, &conn->dst);
+ ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type);
+ ev.status = status;
- mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
+ cmd = find_pairing(conn);
+
+ mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev),
+ cmd ? cmd->sk : NULL);
+
+ if (cmd)
+ pairing_complete(cmd, status);
}
void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index fd32943..25c9040 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -31,9 +31,13 @@
#include "smp.h"
+#define SMP_ALLOW_CMD(smp, code) set_bit(code, &smp->allow_cmd)
+#define SMP_DISALLOW_CMD(smp, code) clear_bit(code, &smp->allow_cmd)
+
#define SMP_TIMEOUT msecs_to_jiffies(30000)
#define AUTH_REQ_MASK 0x07
+#define KEY_DIST_MASK 0x07
enum {
SMP_FLAG_TK_VALID,
@@ -44,7 +48,10 @@ enum {
};
struct smp_chan {
- struct l2cap_conn *conn;
+ struct l2cap_conn *conn;
+ struct delayed_work security_timer;
+ unsigned long allow_cmd; /* Bitmask of allowed commands */
+
u8 preq[7]; /* SMP Pairing Request */
u8 prsp[7]; /* SMP Pairing Response */
u8 prnd[16]; /* SMP Pairing Random (local) */
@@ -139,12 +146,18 @@ static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3])
return 0;
}
-bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16],
- bdaddr_t *bdaddr)
+bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr)
{
+ struct l2cap_chan *chan = hdev->smp_data;
+ struct crypto_blkcipher *tfm;
u8 hash[3];
int err;
+ if (!chan || !chan->data)
+ return false;
+
+ tfm = chan->data;
+
BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
@@ -154,10 +167,17 @@ bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16],
return !memcmp(bdaddr->b, hash, 3);
}
-int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa)
+int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa)
{
+ struct l2cap_chan *chan = hdev->smp_data;
+ struct crypto_blkcipher *tfm;
int err;
+ if (!chan || !chan->data)
+ return -EOPNOTSUPP;
+
+ tfm = chan->data;
+
get_random_bytes(&rpa->b[3], 3);
rpa->b[5] &= 0x3f; /* Clear two most significant bits */
@@ -235,47 +255,38 @@ static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16],
return err;
}
-static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
- u16 dlen, void *data)
+static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, 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;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp;
+ struct kvec iv[2];
+ struct msghdr msg;
- skb = bt_skb_alloc(len, GFP_ATOMIC);
- if (!skb)
- return NULL;
+ if (!chan)
+ return;
- 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);
+ BT_DBG("code 0x%2.2x", code);
- memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
+ iv[0].iov_base = &code;
+ iv[0].iov_len = 1;
- memcpy(skb_put(skb, dlen), data, dlen);
+ iv[1].iov_base = data;
+ iv[1].iov_len = len;
- return skb;
-}
+ memset(&msg, 0, sizeof(msg));
-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);
+ msg.msg_iov = (struct iovec *) &iv;
+ msg.msg_iovlen = 2;
- BT_DBG("code 0x%2.2x", code);
+ l2cap_chan_send(chan, &msg, 1 + len);
- if (!skb)
+ if (!chan->data)
return;
- skb->priority = HCI_PRIO_MAX;
- hci_send_acl(conn->hchan, skb, 0);
+ smp = chan->data;
- cancel_delayed_work_sync(&conn->security_timer);
- schedule_delayed_work(&conn->security_timer, SMP_TIMEOUT);
+ cancel_delayed_work_sync(&smp->security_timer);
+ schedule_delayed_work(&smp->security_timer, SMP_TIMEOUT);
}
static __u8 authreq_to_seclevel(__u8 authreq)
@@ -302,7 +313,8 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
struct smp_cmd_pairing *req,
struct smp_cmd_pairing *rsp, __u8 authreq)
{
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
struct hci_conn *hcon = conn->hcon;
struct hci_dev *hdev = hcon->hdev;
u8 local_dist = 0, remote_dist = 0;
@@ -345,7 +357,8 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
{
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
@@ -356,21 +369,60 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
return 0;
}
+static void smp_chan_destroy(struct l2cap_conn *conn)
+{
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
+ bool complete;
+
+ BUG_ON(!smp);
+
+ cancel_delayed_work_sync(&smp->security_timer);
+
+ complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
+ mgmt_smp_complete(conn->hcon, complete);
+
+ kfree(smp->csrk);
+ kfree(smp->slave_csrk);
+
+ crypto_free_blkcipher(smp->tfm_aes);
+
+ /* If pairing failed clean up any keys we might have */
+ if (!complete) {
+ if (smp->ltk) {
+ list_del(&smp->ltk->list);
+ kfree(smp->ltk);
+ }
+
+ if (smp->slave_ltk) {
+ list_del(&smp->slave_ltk->list);
+ kfree(smp->slave_ltk);
+ }
+
+ if (smp->remote_irk) {
+ list_del(&smp->remote_irk->list);
+ kfree(smp->remote_irk);
+ }
+ }
+
+ chan->data = NULL;
+ kfree(smp);
+ hci_conn_drop(conn->hcon);
+}
+
static void smp_failure(struct l2cap_conn *conn, u8 reason)
{
struct hci_conn *hcon = conn->hcon;
+ struct l2cap_chan *chan = conn->smp;
if (reason)
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
&reason);
clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags);
- mgmt_auth_failed(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type,
- HCI_ERROR_AUTH_FAILURE);
+ mgmt_auth_failed(hcon, HCI_ERROR_AUTH_FAILURE);
- cancel_delayed_work_sync(&conn->security_timer);
-
- if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
+ if (chan->data)
smp_chan_destroy(conn);
}
@@ -405,7 +457,8 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
u8 local_io, u8 remote_io)
{
struct hci_conn *hcon = conn->hcon;
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
u8 method;
u32 passkey = 0;
int ret = 0;
@@ -503,6 +556,11 @@ static u8 smp_confirm(struct smp_chan *smp)
smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
+ if (conn->hcon->out)
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+ else
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
return 0;
}
@@ -574,82 +632,262 @@ static u8 smp_random(struct smp_chan *smp)
return 0;
}
-static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
+static void smp_notify_keys(struct l2cap_conn *conn)
{
- struct smp_chan *smp;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
+ struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ struct smp_cmd_pairing *req = (void *) &smp->preq[1];
+ struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1];
+ bool persistent;
- smp = kzalloc(sizeof(*smp), GFP_ATOMIC);
- if (!smp) {
- clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
- return NULL;
+ if (smp->remote_irk) {
+ mgmt_new_irk(hdev, smp->remote_irk);
+ /* Now that user space can be considered to know the
+ * identity address track the connection based on it
+ * from now on.
+ */
+ bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
+ hcon->dst_type = smp->remote_irk->addr_type;
+ queue_work(hdev->workqueue, &conn->id_addr_update_work);
+
+ /* When receiving an indentity resolving key for
+ * a remote device that does not use a resolvable
+ * private address, just remove the key so that
+ * it is possible to use the controller white
+ * list for scanning.
+ *
+ * Userspace will have been told to not store
+ * this key at this point. So it is safe to
+ * just remove it.
+ */
+ if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
+ list_del(&smp->remote_irk->list);
+ kfree(smp->remote_irk);
+ smp->remote_irk = NULL;
+ }
}
- smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(smp->tfm_aes)) {
- BT_ERR("Unable to create ECB crypto context");
- kfree(smp);
- clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
- return NULL;
+ /* The LTKs and CSRKs should be persistent only if both sides
+ * had the bonding bit set in their authentication requests.
+ */
+ persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING);
+
+ if (smp->csrk) {
+ smp->csrk->bdaddr_type = hcon->dst_type;
+ bacpy(&smp->csrk->bdaddr, &hcon->dst);
+ mgmt_new_csrk(hdev, smp->csrk, persistent);
}
- smp->conn = conn;
- conn->smp_chan = smp;
+ if (smp->slave_csrk) {
+ smp->slave_csrk->bdaddr_type = hcon->dst_type;
+ bacpy(&smp->slave_csrk->bdaddr, &hcon->dst);
+ mgmt_new_csrk(hdev, smp->slave_csrk, persistent);
+ }
- hci_conn_hold(conn->hcon);
+ if (smp->ltk) {
+ smp->ltk->bdaddr_type = hcon->dst_type;
+ bacpy(&smp->ltk->bdaddr, &hcon->dst);
+ mgmt_new_ltk(hdev, smp->ltk, persistent);
+ }
- return smp;
+ if (smp->slave_ltk) {
+ smp->slave_ltk->bdaddr_type = hcon->dst_type;
+ bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
+ mgmt_new_ltk(hdev, smp->slave_ltk, persistent);
+ }
}
-void smp_chan_destroy(struct l2cap_conn *conn)
+static void smp_allow_key_dist(struct smp_chan *smp)
{
- struct smp_chan *smp = conn->smp_chan;
- bool complete;
+ /* Allow the first expected phase 3 PDU. The rest of the PDUs
+ * will be allowed in each PDU handler to ensure we receive
+ * them in the correct order.
+ */
+ if (smp->remote_key_dist & SMP_DIST_ENC_KEY)
+ SMP_ALLOW_CMD(smp, SMP_CMD_ENCRYPT_INFO);
+ else if (smp->remote_key_dist & SMP_DIST_ID_KEY)
+ SMP_ALLOW_CMD(smp, SMP_CMD_IDENT_INFO);
+ else if (smp->remote_key_dist & SMP_DIST_SIGN)
+ SMP_ALLOW_CMD(smp, SMP_CMD_SIGN_INFO);
+}
- BUG_ON(!smp);
+static void smp_distribute_keys(struct smp_chan *smp)
+{
+ struct smp_cmd_pairing *req, *rsp;
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ __u8 *keydist;
- complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
- mgmt_smp_complete(conn->hcon, complete);
+ BT_DBG("conn %p", conn);
- kfree(smp->csrk);
- kfree(smp->slave_csrk);
+ rsp = (void *) &smp->prsp[1];
- crypto_free_blkcipher(smp->tfm_aes);
+ /* The responder sends its keys first */
+ if (hcon->out && (smp->remote_key_dist & KEY_DIST_MASK)) {
+ smp_allow_key_dist(smp);
+ return;
+ }
- /* If pairing failed clean up any keys we might have */
- if (!complete) {
- if (smp->ltk) {
- list_del(&smp->ltk->list);
- kfree(smp->ltk);
- }
+ req = (void *) &smp->preq[1];
- if (smp->slave_ltk) {
- list_del(&smp->slave_ltk->list);
- kfree(smp->slave_ltk);
- }
+ if (hcon->out) {
+ keydist = &rsp->init_key_dist;
+ *keydist &= req->init_key_dist;
+ } else {
+ keydist = &rsp->resp_key_dist;
+ *keydist &= req->resp_key_dist;
+ }
- if (smp->remote_irk) {
- list_del(&smp->remote_irk->list);
- kfree(smp->remote_irk);
+ BT_DBG("keydist 0x%x", *keydist);
+
+ if (*keydist & SMP_DIST_ENC_KEY) {
+ struct smp_cmd_encrypt_info enc;
+ struct smp_cmd_master_ident ident;
+ struct smp_ltk *ltk;
+ u8 authenticated;
+ __le16 ediv;
+ __le64 rand;
+
+ get_random_bytes(enc.ltk, sizeof(enc.ltk));
+ get_random_bytes(&ediv, sizeof(ediv));
+ get_random_bytes(&rand, sizeof(rand));
+
+ smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
+
+ authenticated = hcon->sec_level == BT_SECURITY_HIGH;
+ ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type,
+ SMP_LTK_SLAVE, authenticated, enc.ltk,
+ smp->enc_key_size, ediv, rand);
+ smp->slave_ltk = ltk;
+
+ ident.ediv = ediv;
+ ident.rand = rand;
+
+ smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident);
+
+ *keydist &= ~SMP_DIST_ENC_KEY;
+ }
+
+ if (*keydist & SMP_DIST_ID_KEY) {
+ struct smp_cmd_ident_addr_info addrinfo;
+ struct smp_cmd_ident_info idinfo;
+
+ memcpy(idinfo.irk, hdev->irk, sizeof(idinfo.irk));
+
+ smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo);
+
+ /* The hci_conn contains the local identity address
+ * after the connection has been established.
+ *
+ * This is true even when the connection has been
+ * established using a resolvable random address.
+ */
+ bacpy(&addrinfo.bdaddr, &hcon->src);
+ addrinfo.addr_type = hcon->src_type;
+
+ smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo),
+ &addrinfo);
+
+ *keydist &= ~SMP_DIST_ID_KEY;
+ }
+
+ if (*keydist & SMP_DIST_SIGN) {
+ struct smp_cmd_sign_info sign;
+ struct smp_csrk *csrk;
+
+ /* Generate a new random key */
+ get_random_bytes(sign.csrk, sizeof(sign.csrk));
+
+ csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
+ if (csrk) {
+ csrk->master = 0x00;
+ memcpy(csrk->val, sign.csrk, sizeof(csrk->val));
}
+ smp->slave_csrk = csrk;
+
+ smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign);
+
+ *keydist &= ~SMP_DIST_SIGN;
}
- kfree(smp);
- conn->smp_chan = NULL;
- hci_conn_drop(conn->hcon);
+ /* If there are still keys to be received wait for them */
+ if (smp->remote_key_dist & KEY_DIST_MASK) {
+ smp_allow_key_dist(smp);
+ return;
+ }
+
+ set_bit(SMP_FLAG_COMPLETE, &smp->flags);
+ smp_notify_keys(conn);
+
+ smp_chan_destroy(conn);
+}
+
+static void smp_timeout(struct work_struct *work)
+{
+ struct smp_chan *smp = container_of(work, struct smp_chan,
+ security_timer.work);
+ struct l2cap_conn *conn = smp->conn;
+
+ BT_DBG("conn %p", conn);
+
+ hci_disconnect(conn->hcon, HCI_ERROR_REMOTE_USER_TERM);
+}
+
+static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
+{
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp;
+
+ smp = kzalloc(sizeof(*smp), GFP_ATOMIC);
+ if (!smp)
+ return NULL;
+
+ smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(smp->tfm_aes)) {
+ BT_ERR("Unable to create ECB crypto context");
+ kfree(smp);
+ return NULL;
+ }
+
+ smp->conn = conn;
+ chan->data = smp;
+
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_FAIL);
+
+ INIT_DELAYED_WORK(&smp->security_timer, smp_timeout);
+
+ hci_conn_hold(conn->hcon);
+
+ return smp;
}
int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
{
struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_chan *chan;
struct smp_chan *smp;
u32 value;
+ int err;
BT_DBG("");
- if (!conn || !test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
+ if (!conn)
+ return -ENOTCONN;
+
+ chan = conn->smp;
+ if (!chan)
return -ENOTCONN;
- smp = conn->smp_chan;
+ l2cap_chan_lock(chan);
+ if (!chan->data) {
+ err = -ENOTCONN;
+ goto unlock;
+ }
+
+ smp = chan->data;
switch (mgmt_op) {
case MGMT_OP_USER_PASSKEY_REPLY:
@@ -664,12 +902,16 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
case MGMT_OP_USER_PASSKEY_NEG_REPLY:
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED);
- return 0;
+ err = 0;
+ goto unlock;
default:
smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED);
- return -EOPNOTSUPP;
+ err = -EOPNOTSUPP;
+ goto unlock;
}
+ err = 0;
+
/* If it is our turn to send Pairing Confirm, do so now */
if (test_bit(SMP_FLAG_CFM_PENDING, &smp->flags)) {
u8 rsp = smp_confirm(smp);
@@ -677,12 +919,15 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
smp_failure(conn, rsp);
}
- return 0;
+unlock:
+ l2cap_chan_unlock(chan);
+ return err;
}
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
+ struct l2cap_chan *chan = conn->smp;
struct hci_dev *hdev = conn->hcon->hdev;
struct smp_chan *smp;
u8 key_size, auth, sec_level;
@@ -696,10 +941,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (conn->hcon->role != HCI_ROLE_SLAVE)
return SMP_CMD_NOTSUPP;
- if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
+ if (!chan->data)
smp = smp_chan_create(conn);
else
- smp = conn->smp_chan;
+ smp = chan->data;
if (!smp)
return SMP_UNSPECIFIED;
@@ -708,6 +953,8 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
(req->auth_req & SMP_AUTH_BONDING))
return SMP_PAIRING_NOTSUPP;
+ SMP_DISALLOW_CMD(smp, SMP_CMD_PAIRING_REQ);
+
smp->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&smp->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));
@@ -741,6 +988,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
/* Request setup of TK */
ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
@@ -753,7 +1001,8 @@ 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 *req, *rsp = (void *) skb->data;
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
u8 key_size, auth = SMP_AUTH_NONE;
int ret;
@@ -765,6 +1014,8 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
if (conn->hcon->role != HCI_ROLE_MASTER)
return SMP_CMD_NOTSUPP;
+ SMP_DISALLOW_CMD(smp, SMP_CMD_PAIRING_RSP);
+
skb_pull(skb, sizeof(*rsp));
req = (void *) &smp->preq[1];
@@ -814,20 +1065,27 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
{
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
if (skb->len < sizeof(smp->pcnf))
return SMP_INVALID_PARAMS;
+ SMP_DISALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
skb_pull(skb, sizeof(smp->pcnf));
- if (conn->hcon->out)
+ if (conn->hcon->out) {
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
smp->prnd);
- else if (test_bit(SMP_FLAG_TK_VALID, &smp->flags))
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+ return 0;
+ }
+
+ if (test_bit(SMP_FLAG_TK_VALID, &smp->flags))
return smp_confirm(smp);
else
set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
@@ -837,13 +1095,16 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
{
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
BT_DBG("conn %p", conn);
if (skb->len < sizeof(smp->rrnd))
return SMP_INVALID_PARAMS;
+ SMP_DISALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd));
skb_pull(skb, sizeof(smp->rrnd));
@@ -923,9 +1184,6 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (smp_ltk_encrypt(conn, hcon->pending_sec_level))
return 0;
- if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
- return 0;
-
smp = smp_chan_create(conn);
if (!smp)
return SMP_UNSPECIFIED;
@@ -943,6 +1201,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(&smp->preq[1], &cp, sizeof(cp));
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RSP);
return 0;
}
@@ -950,8 +1209,10 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
{
struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_chan *chan;
struct smp_chan *smp;
__u8 authreq;
+ int ret;
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
@@ -959,6 +1220,8 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
if (!conn)
return 1;
+ chan = conn->smp;
+
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
return 1;
@@ -972,12 +1235,19 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
if (smp_ltk_encrypt(conn, hcon->pending_sec_level))
return 0;
- if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
- return 0;
+ l2cap_chan_lock(chan);
+
+ /* If SMP is already in progress ignore this request */
+ if (chan->data) {
+ ret = 0;
+ goto unlock;
+ }
smp = smp_chan_create(conn);
- if (!smp)
- return 1;
+ if (!smp) {
+ ret = 1;
+ goto unlock;
+ }
authreq = seclevel_to_authreq(sec_level);
@@ -996,30 +1266,35 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
memcpy(&smp->preq[1], &cp, sizeof(cp));
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RSP);
} else {
struct smp_cmd_security_req cp;
cp.auth_req = authreq;
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_REQ);
}
set_bit(SMP_FLAG_INITIATOR, &smp->flags);
+ ret = 0;
- return 0;
+unlock:
+ l2cap_chan_unlock(chan);
+ return ret;
}
static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_encrypt_info *rp = (void *) skb->data;
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
BT_DBG("conn %p", conn);
if (skb->len < sizeof(*rp))
return SMP_INVALID_PARAMS;
- /* Ignore this PDU if it wasn't requested */
- if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
- return 0;
+ SMP_DISALLOW_CMD(smp, SMP_CMD_ENCRYPT_INFO);
+ SMP_ALLOW_CMD(smp, SMP_CMD_MASTER_IDENT);
skb_pull(skb, sizeof(*rp));
@@ -1031,7 +1306,8 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_master_ident *rp = (void *) skb->data;
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
struct hci_dev *hdev = conn->hcon->hdev;
struct hci_conn *hcon = conn->hcon;
struct smp_ltk *ltk;
@@ -1042,13 +1318,13 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
if (skb->len < sizeof(*rp))
return SMP_INVALID_PARAMS;
- /* Ignore this PDU if it wasn't requested */
- if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
- return 0;
-
/* Mark the information as received */
smp->remote_key_dist &= ~SMP_DIST_ENC_KEY;
+ SMP_DISALLOW_CMD(smp, SMP_CMD_MASTER_IDENT);
+ if (smp->remote_key_dist & SMP_DIST_ID_KEY)
+ SMP_ALLOW_CMD(smp, SMP_CMD_IDENT_INFO);
+
skb_pull(skb, sizeof(*rp));
hci_dev_lock(hdev);
@@ -1057,8 +1333,8 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
authenticated, smp->tk, smp->enc_key_size,
rp->ediv, rp->rand);
smp->ltk = ltk;
- if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
- smp_distribute_keys(conn);
+ if (!(smp->remote_key_dist & KEY_DIST_MASK))
+ smp_distribute_keys(smp);
hci_dev_unlock(hdev);
return 0;
@@ -1067,16 +1343,16 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
static int smp_cmd_ident_info(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_ident_info *info = (void *) skb->data;
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
BT_DBG("");
if (skb->len < sizeof(*info))
return SMP_INVALID_PARAMS;
- /* Ignore this PDU if it wasn't requested */
- if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
- return 0;
+ SMP_DISALLOW_CMD(smp, SMP_CMD_IDENT_INFO);
+ SMP_ALLOW_CMD(smp, SMP_CMD_IDENT_ADDR_INFO);
skb_pull(skb, sizeof(*info));
@@ -1089,7 +1365,8 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
struct sk_buff *skb)
{
struct smp_cmd_ident_addr_info *info = (void *) skb->data;
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
struct hci_conn *hcon = conn->hcon;
bdaddr_t rpa;
@@ -1098,13 +1375,13 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
if (skb->len < sizeof(*info))
return SMP_INVALID_PARAMS;
- /* Ignore this PDU if it wasn't requested */
- if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
- return 0;
-
/* Mark the information as received */
smp->remote_key_dist &= ~SMP_DIST_ID_KEY;
+ SMP_DISALLOW_CMD(smp, SMP_CMD_IDENT_ADDR_INFO);
+ if (smp->remote_key_dist & SMP_DIST_SIGN)
+ SMP_ALLOW_CMD(smp, SMP_CMD_SIGN_INFO);
+
skb_pull(skb, sizeof(*info));
hci_dev_lock(hcon->hdev);
@@ -1133,7 +1410,8 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
smp->id_addr_type, smp->irk, &rpa);
distribute:
- smp_distribute_keys(conn);
+ if (!(smp->remote_key_dist & KEY_DIST_MASK))
+ smp_distribute_keys(smp);
hci_dev_unlock(hcon->hdev);
@@ -1143,7 +1421,8 @@ distribute:
static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_sign_info *rp = (void *) skb->data;
- struct smp_chan *smp = conn->smp_chan;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
struct hci_dev *hdev = conn->hcon->hdev;
struct smp_csrk *csrk;
@@ -1152,13 +1431,11 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
if (skb->len < sizeof(*rp))
return SMP_INVALID_PARAMS;
- /* Ignore this PDU if it wasn't requested */
- if (!(smp->remote_key_dist & SMP_DIST_SIGN))
- return 0;
-
/* Mark the information as received */
smp->remote_key_dist &= ~SMP_DIST_SIGN;
+ SMP_DISALLOW_CMD(smp, SMP_CMD_SIGN_INFO);
+
skb_pull(skb, sizeof(*rp));
hci_dev_lock(hdev);
@@ -1168,16 +1445,17 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(csrk->val, rp->csrk, sizeof(csrk->val));
}
smp->csrk = csrk;
- if (!(smp->remote_key_dist & SMP_DIST_SIGN))
- smp_distribute_keys(conn);
+ smp_distribute_keys(smp);
hci_dev_unlock(hdev);
return 0;
}
-int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb)
{
+ struct l2cap_conn *conn = chan->conn;
struct hci_conn *hcon = conn->hcon;
+ struct smp_chan *smp;
__u8 code, reason;
int err = 0;
@@ -1186,13 +1464,10 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
return 0;
}
- if (skb->len < 1) {
- kfree_skb(skb);
+ if (skb->len < 1)
return -EILSEQ;
- }
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) {
- err = -EOPNOTSUPP;
reason = SMP_PAIRING_NOTSUPP;
goto done;
}
@@ -1200,18 +1475,19 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
code = skb->data[0];
skb_pull(skb, sizeof(code));
- /*
- * The SMP context must be initialized for all other PDUs except
- * pairing and security requests. If we get any other PDU when
- * not initialized simply disconnect (done if this function
- * returns an error).
+ smp = chan->data;
+
+ if (code > SMP_CMD_MAX)
+ goto drop;
+
+ if (smp && !test_bit(code, &smp->allow_cmd))
+ goto drop;
+
+ /* If we don't have a context the only allowed commands are
+ * pairing request and security request.
*/
- if (code != SMP_CMD_PAIRING_REQ && code != SMP_CMD_SECURITY_REQ &&
- !conn->smp_chan) {
- BT_ERR("Unexpected SMP command 0x%02x. Disconnecting.", code);
- kfree_skb(skb);
- return -EOPNOTSUPP;
- }
+ if (!smp && code != SMP_CMD_PAIRING_REQ && code != SMP_CMD_SECURITY_REQ)
+ goto drop;
switch (code) {
case SMP_CMD_PAIRING_REQ:
@@ -1220,7 +1496,6 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
case SMP_CMD_PAIRING_FAIL:
smp_failure(conn, 0);
- reason = 0;
err = -EPERM;
break;
@@ -1262,197 +1537,217 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
default:
BT_DBG("Unknown command code 0x%2.2x", code);
-
reason = SMP_CMD_NOTSUPP;
- err = -EOPNOTSUPP;
goto done;
}
done:
- if (reason)
- smp_failure(conn, reason);
+ if (!err) {
+ if (reason)
+ smp_failure(conn, reason);
+ kfree_skb(skb);
+ }
- kfree_skb(skb);
return err;
+
+drop:
+ BT_ERR("%s unexpected SMP command 0x%02x from %pMR", hcon->hdev->name,
+ code, &hcon->dst);
+ kfree_skb(skb);
+ return 0;
}
-static void smp_notify_keys(struct l2cap_conn *conn)
+static void smp_teardown_cb(struct l2cap_chan *chan, int err)
{
- struct smp_chan *smp = conn->smp_chan;
- struct hci_conn *hcon = conn->hcon;
- struct hci_dev *hdev = hcon->hdev;
- struct smp_cmd_pairing *req = (void *) &smp->preq[1];
- struct smp_cmd_pairing *rsp = (void *) &smp->prsp[1];
- bool persistent;
+ struct l2cap_conn *conn = chan->conn;
- if (smp->remote_irk) {
- mgmt_new_irk(hdev, smp->remote_irk);
- /* Now that user space can be considered to know the
- * identity address track the connection based on it
- * from now on.
- */
- bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
- hcon->dst_type = smp->remote_irk->addr_type;
- l2cap_conn_update_id_addr(hcon);
+ BT_DBG("chan %p", chan);
- /* When receiving an indentity resolving key for
- * a remote device that does not use a resolvable
- * private address, just remove the key so that
- * it is possible to use the controller white
- * list for scanning.
- *
- * Userspace will have been told to not store
- * this key at this point. So it is safe to
- * just remove it.
- */
- if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
- list_del(&smp->remote_irk->list);
- kfree(smp->remote_irk);
- smp->remote_irk = NULL;
- }
- }
+ if (chan->data)
+ smp_chan_destroy(conn);
- /* The LTKs and CSRKs should be persistent only if both sides
- * had the bonding bit set in their authentication requests.
- */
- persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING);
+ conn->smp = NULL;
+ l2cap_chan_put(chan);
+}
- if (smp->csrk) {
- smp->csrk->bdaddr_type = hcon->dst_type;
- bacpy(&smp->csrk->bdaddr, &hcon->dst);
- mgmt_new_csrk(hdev, smp->csrk, persistent);
- }
+static void smp_resume_cb(struct l2cap_chan *chan)
+{
+ struct smp_chan *smp = chan->data;
+ struct l2cap_conn *conn = chan->conn;
+ struct hci_conn *hcon = conn->hcon;
- if (smp->slave_csrk) {
- smp->slave_csrk->bdaddr_type = hcon->dst_type;
- bacpy(&smp->slave_csrk->bdaddr, &hcon->dst);
- mgmt_new_csrk(hdev, smp->slave_csrk, persistent);
- }
+ BT_DBG("chan %p", chan);
- if (smp->ltk) {
- smp->ltk->bdaddr_type = hcon->dst_type;
- bacpy(&smp->ltk->bdaddr, &hcon->dst);
- mgmt_new_ltk(hdev, smp->ltk, persistent);
- }
+ if (!smp)
+ return;
- if (smp->slave_ltk) {
- smp->slave_ltk->bdaddr_type = hcon->dst_type;
- bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
- mgmt_new_ltk(hdev, smp->slave_ltk, persistent);
- }
+ if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+ return;
+
+ cancel_delayed_work(&smp->security_timer);
+
+ smp_distribute_keys(smp);
}
-int smp_distribute_keys(struct l2cap_conn *conn)
+static void smp_ready_cb(struct l2cap_chan *chan)
{
- struct smp_cmd_pairing *req, *rsp;
- struct smp_chan *smp = conn->smp_chan;
- struct hci_conn *hcon = conn->hcon;
- struct hci_dev *hdev = hcon->hdev;
- __u8 *keydist;
+ struct l2cap_conn *conn = chan->conn;
- BT_DBG("conn %p", conn);
+ BT_DBG("chan %p", chan);
- if (!test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags))
- return 0;
+ conn->smp = chan;
+ l2cap_chan_hold(chan);
+}
- rsp = (void *) &smp->prsp[1];
+static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+ int err;
- /* The responder sends its keys first */
- if (hcon->out && (smp->remote_key_dist & 0x07))
- return 0;
+ BT_DBG("chan %p", chan);
- req = (void *) &smp->preq[1];
+ err = smp_sig_channel(chan, skb);
+ if (err) {
+ struct smp_chan *smp = chan->data;
- if (hcon->out) {
- keydist = &rsp->init_key_dist;
- *keydist &= req->init_key_dist;
- } else {
- keydist = &rsp->resp_key_dist;
- *keydist &= req->resp_key_dist;
+ if (smp)
+ cancel_delayed_work_sync(&smp->security_timer);
+
+ hci_disconnect(chan->conn->hcon, HCI_ERROR_AUTH_FAILURE);
}
- BT_DBG("keydist 0x%x", *keydist);
+ return err;
+}
- if (*keydist & SMP_DIST_ENC_KEY) {
- struct smp_cmd_encrypt_info enc;
- struct smp_cmd_master_ident ident;
- struct smp_ltk *ltk;
- u8 authenticated;
- __le16 ediv;
- __le64 rand;
+static struct sk_buff *smp_alloc_skb_cb(struct l2cap_chan *chan,
+ unsigned long hdr_len,
+ unsigned long len, int nb)
+{
+ struct sk_buff *skb;
- get_random_bytes(enc.ltk, sizeof(enc.ltk));
- get_random_bytes(&ediv, sizeof(ediv));
- get_random_bytes(&rand, sizeof(rand));
+ skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
- smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
+ skb->priority = HCI_PRIO_MAX;
+ bt_cb(skb)->chan = chan;
- authenticated = hcon->sec_level == BT_SECURITY_HIGH;
- ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type,
- SMP_LTK_SLAVE, authenticated, enc.ltk,
- smp->enc_key_size, ediv, rand);
- smp->slave_ltk = ltk;
+ return skb;
+}
- ident.ediv = ediv;
- ident.rand = rand;
+static const struct l2cap_ops smp_chan_ops = {
+ .name = "Security Manager",
+ .ready = smp_ready_cb,
+ .recv = smp_recv_cb,
+ .alloc_skb = smp_alloc_skb_cb,
+ .teardown = smp_teardown_cb,
+ .resume = smp_resume_cb,
+
+ .new_connection = l2cap_chan_no_new_connection,
+ .state_change = l2cap_chan_no_state_change,
+ .close = l2cap_chan_no_close,
+ .defer = l2cap_chan_no_defer,
+ .suspend = l2cap_chan_no_suspend,
+ .set_shutdown = l2cap_chan_no_set_shutdown,
+ .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
+ .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
+};
- smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident);
+static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
+{
+ struct l2cap_chan *chan;
- *keydist &= ~SMP_DIST_ENC_KEY;
- }
+ BT_DBG("pchan %p", pchan);
- if (*keydist & SMP_DIST_ID_KEY) {
- struct smp_cmd_ident_addr_info addrinfo;
- struct smp_cmd_ident_info idinfo;
+ chan = l2cap_chan_create();
+ if (!chan)
+ return NULL;
- memcpy(idinfo.irk, hdev->irk, sizeof(idinfo.irk));
+ chan->chan_type = pchan->chan_type;
+ chan->ops = &smp_chan_ops;
+ chan->scid = pchan->scid;
+ chan->dcid = chan->scid;
+ chan->imtu = pchan->imtu;
+ chan->omtu = pchan->omtu;
+ chan->mode = pchan->mode;
- smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo);
+ BT_DBG("created chan %p", chan);
- /* The hci_conn contains the local identity address
- * after the connection has been established.
- *
- * This is true even when the connection has been
- * established using a resolvable random address.
- */
- bacpy(&addrinfo.bdaddr, &hcon->src);
- addrinfo.addr_type = hcon->src_type;
+ return chan;
+}
- smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo),
- &addrinfo);
+static const struct l2cap_ops smp_root_chan_ops = {
+ .name = "Security Manager Root",
+ .new_connection = smp_new_conn_cb,
+
+ /* None of these are implemented for the root channel */
+ .close = l2cap_chan_no_close,
+ .alloc_skb = l2cap_chan_no_alloc_skb,
+ .recv = l2cap_chan_no_recv,
+ .state_change = l2cap_chan_no_state_change,
+ .teardown = l2cap_chan_no_teardown,
+ .ready = l2cap_chan_no_ready,
+ .defer = l2cap_chan_no_defer,
+ .suspend = l2cap_chan_no_suspend,
+ .resume = l2cap_chan_no_resume,
+ .set_shutdown = l2cap_chan_no_set_shutdown,
+ .get_sndtimeo = l2cap_chan_no_get_sndtimeo,
+ .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
+};
- *keydist &= ~SMP_DIST_ID_KEY;
- }
+int smp_register(struct hci_dev *hdev)
+{
+ struct l2cap_chan *chan;
+ struct crypto_blkcipher *tfm_aes;
- if (*keydist & SMP_DIST_SIGN) {
- struct smp_cmd_sign_info sign;
- struct smp_csrk *csrk;
+ BT_DBG("%s", hdev->name);
- /* Generate a new random key */
- get_random_bytes(sign.csrk, sizeof(sign.csrk));
+ tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm_aes)) {
+ int err = PTR_ERR(tfm_aes);
+ BT_ERR("Unable to create crypto context");
+ return err;
+ }
- csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
- if (csrk) {
- csrk->master = 0x00;
- memcpy(csrk->val, sign.csrk, sizeof(csrk->val));
- }
- smp->slave_csrk = csrk;
+ chan = l2cap_chan_create();
+ if (!chan) {
+ crypto_free_blkcipher(tfm_aes);
+ return -ENOMEM;
+ }
- smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign);
+ chan->data = tfm_aes;
- *keydist &= ~SMP_DIST_SIGN;
- }
+ l2cap_add_scid(chan, L2CAP_CID_SMP);
- /* If there are still keys to be received wait for them */
- if ((smp->remote_key_dist & 0x07))
- return 0;
+ l2cap_chan_set_defaults(chan);
- clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags);
- cancel_delayed_work_sync(&conn->security_timer);
- set_bit(SMP_FLAG_COMPLETE, &smp->flags);
- smp_notify_keys(conn);
+ bacpy(&chan->src, &hdev->bdaddr);
+ chan->src_type = BDADDR_LE_PUBLIC;
+ chan->state = BT_LISTEN;
+ chan->mode = L2CAP_MODE_BASIC;
+ chan->imtu = L2CAP_DEFAULT_MTU;
+ chan->ops = &smp_root_chan_ops;
- smp_chan_destroy(conn);
+ hdev->smp_data = chan;
return 0;
}
+
+void smp_unregister(struct hci_dev *hdev)
+{
+ struct l2cap_chan *chan = hdev->smp_data;
+ struct crypto_blkcipher *tfm_aes;
+
+ if (!chan)
+ return;
+
+ BT_DBG("%s chan %p", hdev->name, chan);
+
+ tfm_aes = chan->data;
+ if (tfm_aes) {
+ chan->data = NULL;
+ crypto_free_blkcipher(tfm_aes);
+ }
+
+ hdev->smp_data = NULL;
+ l2cap_chan_put(chan);
+}
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
index 796f4f4..5240537 100644
--- a/net/bluetooth/smp.h
+++ b/net/bluetooth/smp.h
@@ -102,6 +102,8 @@ struct smp_cmd_security_req {
__u8 auth_req;
} __packed;
+#define SMP_CMD_MAX 0x0b
+
#define SMP_PASSKEY_ENTRY_FAILED 0x01
#define SMP_OOB_NOT_AVAIL 0x02
#define SMP_AUTH_REQUIREMENTS 0x03
@@ -126,14 +128,12 @@ enum {
/* SMP Commands */
bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
-int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
-int smp_distribute_keys(struct l2cap_conn *conn);
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
-void smp_chan_destroy(struct l2cap_conn *conn);
+bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr);
+int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa);
-bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16],
- bdaddr_t *bdaddr);
-int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa);
+int smp_register(struct hci_dev *hdev);
+void smp_unregister(struct hci_dev *hdev);
#endif /* __SMP_H */
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c
index 016b77e..5e788cd 100644
--- a/net/ieee802154/6lowpan_rtnl.c
+++ b/net/ieee802154/6lowpan_rtnl.c
@@ -77,14 +77,6 @@ lowpan_dev_info *lowpan_dev_info(const struct net_device *dev)
return netdev_priv(dev);
}
-static inline void lowpan_address_flip(u8 *src, u8 *dest)
-{
- int i;
-
- for (i = 0; i < IEEE802154_ADDR_LEN; i++)
- (dest)[IEEE802154_ADDR_LEN - i - 1] = (src)[i];
-}
-
static int lowpan_header_create(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *_daddr,
const void *_saddr, unsigned int len)
@@ -246,7 +238,7 @@ lowpan_alloc_frag(struct sk_buff *skb, int size,
return ERR_PTR(-rc);
}
} else {
- frag = ERR_PTR(ENOMEM);
+ frag = ERR_PTR(-ENOMEM);
}
return frag;
@@ -437,7 +429,7 @@ static void lowpan_setup(struct net_device *dev)
/* Frame Control + Sequence Number + Address fields + Security Header */
dev->hard_header_len = 2 + 1 + 20 + 14;
dev->needed_tailroom = 2; /* FCS */
- dev->mtu = 1281;
+ dev->mtu = IPV6_MIN_MTU;
dev->tx_queue_len = 0;
dev->flags = IFF_BROADCAST | IFF_MULTICAST;
dev->watchdog_timeo = 0;
diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c
index ffec6ce..32755cb 100644
--- a/net/ieee802154/reassembly.c
+++ b/net/ieee802154/reassembly.c
@@ -355,8 +355,6 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type)
struct net *net = dev_net(skb->dev);
struct lowpan_frag_info *frag_info = lowpan_cb(skb);
struct ieee802154_addr source, dest;
- struct netns_ieee802154_lowpan *ieee802154_lowpan =
- net_ieee802154_lowpan(net);
int err;
source = mac_cb(skb)->source;
@@ -366,8 +364,10 @@ int lowpan_frag_rcv(struct sk_buff *skb, const u8 frag_type)
if (err < 0)
goto err;
- if (frag_info->d_size > ieee802154_lowpan->max_dsize)
+ if (frag_info->d_size > IPV6_MIN_MTU) {
+ net_warn_ratelimited("lowpan_frag_rcv: datagram size exceeds MTU\n");
goto err;
+ }
fq = fq_find(net, frag_info, &source, &dest);
if (fq != NULL) {
@@ -415,13 +415,6 @@ static struct ctl_table lowpan_frags_ns_ctl_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
- {
- .procname = "6lowpanfrag_max_datagram_size",
- .data = &init_net.ieee802154_lowpan.max_dsize,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec
- },
{ }
};
@@ -458,7 +451,6 @@ static int __net_init lowpan_frags_ns_sysctl_register(struct net *net)
table[1].data = &ieee802154_lowpan->frags.low_thresh;
table[1].extra2 = &ieee802154_lowpan->frags.high_thresh;
table[2].data = &ieee802154_lowpan->frags.timeout;
- table[3].data = &ieee802154_lowpan->max_dsize;
/* Don't export sysctls to unprivileged users */
if (net->user_ns != &init_user_ns)
@@ -533,7 +525,6 @@ static int __net_init lowpan_frags_init_net(struct net *net)
ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH;
ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT;
- ieee802154_lowpan->max_dsize = 0xFFFF;
inet_frags_init_net(&ieee802154_lowpan->frags);
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index cb4459b..76b7f5e 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -643,7 +643,7 @@ static int fib6_commit_metrics(struct dst_entry *dst,
if (dst->flags & DST_HOST) {
mp = dst_metrics_write_ptr(dst);
} else {
- mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
+ mp = kzalloc(sizeof(u32) * RTAX_MAX, GFP_ATOMIC);
if (!mp)
return -ENOMEM;
dst_init_metrics(dst, mp, 0);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index bd01a9f..4c74e8d 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -541,6 +541,8 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
continue;
if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
continue;
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ continue;
if (!compat)
compat = &sdata->vif.bss_conf.chandef;
@@ -1351,7 +1353,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
list_del(&sdata->reserved_chanctx_list);
list_move(&sdata->assigned_chanctx_list,
- &new_ctx->assigned_vifs);
+ &ctx->assigned_vifs);
sdata->reserved_chanctx = NULL;
ieee80211_vif_chanctx_reservation_complete(sdata);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index b5e0a53..bafe489 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -169,7 +169,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf) + buf - p, "next dialog_token: %#02x\n",
sta->ampdu_mlme.dialog_token_allocator + 1);
p += scnprintf(p, sizeof(buf) + buf - p,
- "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n");
+ "TID\t\tRX\tDTKN\tSSN\t\tTX\tDTKN\tpending\n");
for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]);
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 8f0887f..b488e18 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -957,7 +957,8 @@ mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
if (!matches_local)
event = CNF_RJCT;
if (!mesh_plink_free_count(sdata) ||
- (sta->llid != llid || sta->plid != plid))
+ sta->llid != llid ||
+ (sta->plid && sta->plid != plid))
event = CNF_IGNR;
else
event = CNF_ACPT;
@@ -1074,6 +1075,10 @@ mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
goto unlock_rcu;
}
+ /* 802.11-2012 13.3.7.2 - update plid on CNF if not set */
+ if (!sta->plid && event == CNF_ACPT)
+ sta->plid = plid;
+
changed |= mesh_plink_fsm(sdata, sta, event);
unlock_rcu:
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 9d4ccb2..2de8870 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -4461,8 +4461,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
if (bss->wmm_used && bss->uapsd_supported &&
- (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) &&
- sdata->wmm_acm != 0xff) {
+ (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
assoc_data->uapsd = true;
ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
} else {
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 215752e..90395c6 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1095,8 +1095,11 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
unsigned long flags;
struct ps_data *ps;
- if (sdata->vif.type == NL80211_IFTYPE_AP ||
- sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
+ u.ap);
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
ps = &sdata->bss->ps;
else if (ieee80211_vif_is_mesh(&sdata->vif))
ps = &sdata->u.mesh.ps;
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index 7f820a1..a14cf9e 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -86,9 +86,8 @@ fail:
static void mac802154_rx_worker(struct work_struct *work)
{
struct rx_work *rw = container_of(work, struct rx_work, work);
- struct sk_buff *skb = rw->skb;
- mac802154_subif_rx(rw->dev, skb, rw->lqi);
+ mac802154_subif_rx(rw->dev, rw->skb, rw->lqi);
kfree(rw);
}
@@ -101,7 +100,7 @@ ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
if (!skb)
return;
- work = kzalloc(sizeof(struct rx_work), GFP_ATOMIC);
+ work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work)
return;
diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c
index 8124353..fdf4c0e6 100644
--- a/net/mac802154/tx.c
+++ b/net/mac802154/tx.c
@@ -89,8 +89,7 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
if (!(priv->phy->channels_supported[page] & (1 << chan))) {
WARN_ON(1);
- kfree_skb(skb);
- return NETDEV_TX_OK;
+ goto err_tx;
}
mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb);
@@ -103,12 +102,10 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
data[1] = crc >> 8;
}
- if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) {
- kfree_skb(skb);
- return NETDEV_TX_OK;
- }
+ if (skb_cow_head(skb, priv->hw.extra_tx_headroom))
+ goto err_tx;
- work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC);
+ work = kzalloc(sizeof(*work), GFP_ATOMIC);
if (!work) {
kfree_skb(skb);
return NETDEV_TX_BUSY;
@@ -129,4 +126,8 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
queue_work(priv->dev_workqueue, &work->work);
return NETDEV_TX_OK;
+
+err_tx:
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
}
diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c
index 3c3069f..b796112 100644
--- a/net/mac802154/wpan.c
+++ b/net/mac802154/wpan.c
@@ -462,7 +462,10 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb,
skb->pkt_type = PACKET_OTHERHOST;
break;
default:
- break;
+ spin_unlock_bh(&sdata->mib_lock);
+ pr_debug("invalid dest mode\n");
+ kfree_skb(skb);
+ return NET_RX_DROP;
}
spin_unlock_bh(&sdata->mib_lock);
@@ -472,8 +475,7 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb,
rc = mac802154_llsec_decrypt(&sdata->sec, skb);
if (rc) {
pr_debug("decryption failed: %i\n", rc);
- kfree_skb(skb);
- return NET_RX_DROP;
+ goto fail;
}
sdata->dev->stats.rx_packets++;
@@ -485,9 +487,12 @@ mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb,
default:
pr_warn("ieee802154: bad frame received (type = %d)\n",
mac_cb(skb)->type);
- kfree_skb(skb);
- return NET_RX_DROP;
+ goto fail;
}
+
+fail:
+ kfree_skb(skb);
+ return NET_RX_DROP;
}
static void mac802154_print_addr(const char *name,
@@ -573,6 +578,7 @@ void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
ret = mac802154_parse_frame_start(skb, &hdr);
if (ret) {
pr_debug("got invalid frame\n");
+ kfree_skb(skb);
return;
}
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index fe5cda0..5231652 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -42,6 +42,9 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
static int make_writable(struct sk_buff *skb, int write_len)
{
+ if (!pskb_may_pull(skb, write_len))
+ return -ENOMEM;
+
if (!skb_cloned(skb) || skb_clone_writable(skb, write_len))
return 0;
@@ -70,6 +73,8 @@ static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
vlan_set_encap_proto(skb, vhdr);
skb->mac_header += VLAN_HLEN;
+ if (skb_network_offset(skb) < ETH_HLEN)
+ skb_set_network_header(skb, ETH_HLEN);
skb_reset_mac_len(skb);
return 0;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 8d9f804..93896d2 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -632,6 +632,7 @@ static void init_prb_bdqc(struct packet_sock *po,
p1->tov_in_jiffies = msecs_to_jiffies(p1->retire_blk_tov);
p1->blk_sizeof_priv = req_u->req3.tp_sizeof_priv;
+ p1->max_frame_len = p1->kblk_size - BLK_PLUS_PRIV(p1->blk_sizeof_priv);
prb_init_ft_ops(p1, req_u);
prb_setup_retire_blk_timer(po, tx_ring);
prb_open_block(p1, pbd);
@@ -1942,6 +1943,18 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
if ((int)snaplen < 0)
snaplen = 0;
}
+ } else if (unlikely(macoff + snaplen >
+ GET_PBDQC_FROM_RB(&po->rx_ring)->max_frame_len)) {
+ u32 nval;
+
+ nval = GET_PBDQC_FROM_RB(&po->rx_ring)->max_frame_len - macoff;
+ pr_err_once("tpacket_rcv: packet too big, clamped from %u to %u. macoff=%u\n",
+ snaplen, nval, macoff);
+ snaplen = nval;
+ if (unlikely((int)snaplen < 0)) {
+ snaplen = 0;
+ macoff = GET_PBDQC_FROM_RB(&po->rx_ring)->max_frame_len;
+ }
}
spin_lock(&sk->sk_receive_queue.lock);
h.raw = packet_current_rx_frame(po, skb,
@@ -3783,6 +3796,10 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
goto out;
if (unlikely(req->tp_block_size & (PAGE_SIZE - 1)))
goto out;
+ if (po->tp_version >= TPACKET_V3 &&
+ (int)(req->tp_block_size -
+ BLK_PLUS_PRIV(req_u->req3.tp_sizeof_priv)) <= 0)
+ goto out;
if (unlikely(req->tp_frame_size < po->tp_hdrlen +
po->tp_reserve))
goto out;
diff --git a/net/packet/internal.h b/net/packet/internal.h
index eb9580a..cdddf6a 100644
--- a/net/packet/internal.h
+++ b/net/packet/internal.h
@@ -29,6 +29,7 @@ struct tpacket_kbdq_core {
char *pkblk_start;
char *pkblk_end;
int kblk_size;
+ unsigned int max_frame_len;
unsigned int knum_blocks;
uint64_t knxt_seq_num;
char *prev;
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index ead5264..762a04b 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -159,7 +159,6 @@ struct cbq_sched_data {
struct cbq_class *tx_borrowed;
int tx_len;
psched_time_t now; /* Cached timestamp */
- psched_time_t now_rt; /* Cached real time */
unsigned int pmask;
struct hrtimer delay_timer;
@@ -353,12 +352,7 @@ cbq_mark_toplevel(struct cbq_sched_data *q, struct cbq_class *cl)
int toplevel = q->toplevel;
if (toplevel > cl->level && !(qdisc_is_throttled(cl->q))) {
- psched_time_t now;
- psched_tdiff_t incr;
-
- now = psched_get_time();
- incr = now - q->now_rt;
- now = q->now + incr;
+ psched_time_t now = psched_get_time();
do {
if (cl->undertime < now) {
@@ -700,8 +694,13 @@ cbq_update(struct cbq_sched_data *q)
struct cbq_class *this = q->tx_class;
struct cbq_class *cl = this;
int len = q->tx_len;
+ psched_time_t now;
q->tx_class = NULL;
+ /* Time integrator. We calculate EOS time
+ * by adding expected packet transmission time.
+ */
+ now = q->now + L2T(&q->link, len);
for ( ; cl; cl = cl->share) {
long avgidle = cl->avgidle;
@@ -717,7 +716,7 @@ cbq_update(struct cbq_sched_data *q)
* idle = (now - last) - last_pktlen/rate
*/
- idle = q->now - cl->last;
+ idle = now - cl->last;
if ((unsigned long)idle > 128*1024*1024) {
avgidle = cl->maxidle;
} else {
@@ -761,7 +760,7 @@ cbq_update(struct cbq_sched_data *q)
idle -= L2T(&q->link, len);
idle += L2T(cl, len);
- cl->undertime = q->now + idle;
+ cl->undertime = now + idle;
} else {
/* Underlimit */
@@ -771,7 +770,8 @@ cbq_update(struct cbq_sched_data *q)
else
cl->avgidle = avgidle;
}
- cl->last = q->now;
+ if ((s64)(now - cl->last) > 0)
+ cl->last = now;
}
cbq_update_toplevel(q, this, q->tx_borrowed);
@@ -943,31 +943,13 @@ cbq_dequeue(struct Qdisc *sch)
struct sk_buff *skb;
struct cbq_sched_data *q = qdisc_priv(sch);
psched_time_t now;
- psched_tdiff_t incr;
now = psched_get_time();
- incr = now - q->now_rt;
-
- if (q->tx_class) {
- psched_tdiff_t incr2;
- /* Time integrator. We calculate EOS time
- * by adding expected packet transmission time.
- * If real time is greater, we warp artificial clock,
- * so that:
- *
- * cbq_time = max(real_time, work);
- */
- incr2 = L2T(&q->link, q->tx_len);
- q->now += incr2;
+
+ if (q->tx_class)
cbq_update(q);
- if ((incr -= incr2) < 0)
- incr = 0;
- q->now += incr;
- } else {
- if (now > q->now)
- q->now = now;
- }
- q->now_rt = now;
+
+ q->now = now;
for (;;) {
q->wd_expires = 0;
@@ -1223,7 +1205,6 @@ cbq_reset(struct Qdisc *sch)
hrtimer_cancel(&q->delay_timer);
q->toplevel = TC_CBQ_MAXLEVEL;
q->now = psched_get_time();
- q->now_rt = q->now;
for (prio = 0; prio <= TC_CBQ_MAXPRIO; prio++)
q->active[prio] = NULL;
@@ -1407,7 +1388,6 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt)
q->delay_timer.function = cbq_undelay;
q->toplevel = TC_CBQ_MAXLEVEL;
q->now = psched_get_time();
- q->now_rt = q->now;
cbq_link_class(&q->link);
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 06a9ee6..a88b852 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -813,6 +813,7 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
else {
dst_release(transport->dst);
transport->dst = NULL;
+ ulp_notify = false;
}
spc_state = SCTP_ADDR_UNREACHABLE;
@@ -1244,7 +1245,7 @@ static struct sctp_transport *sctp_trans_elect_best(struct sctp_transport *curr,
{
u8 score_curr, score_best;
- if (best == NULL)
+ if (best == NULL || curr == best)
return curr;
score_curr = sctp_trans_score(curr);
@@ -1355,14 +1356,11 @@ static void sctp_select_active_and_retran_path(struct sctp_association *asoc)
trans_sec = trans_pri;
/* If we failed to find a usable transport, just camp on the
- * primary or retran, even if they are inactive, if possible
- * pick a PF iff it's the better choice.
+ * active or pick a PF iff it's the better choice.
*/
if (trans_pri == NULL) {
- trans_pri = sctp_trans_elect_best(asoc->peer.primary_path,
- asoc->peer.retran_path);
- trans_pri = sctp_trans_elect_best(trans_pri, trans_pf);
- trans_sec = asoc->peer.primary_path;
+ trans_pri = sctp_trans_elect_best(asoc->peer.active_path, trans_pf);
+ trans_sec = trans_pri;
}
/* Set the active and retran transports. */
diff --git a/net/tipc/port.h b/net/tipc/port.h
index 3f93454..3087da3 100644
--- a/net/tipc/port.h
+++ b/net/tipc/port.h
@@ -179,9 +179,12 @@ static inline int tipc_port_importance(struct tipc_port *port)
return msg_importance(&port->phdr);
}
-static inline void tipc_port_set_importance(struct tipc_port *port, int imp)
+static inline int tipc_port_set_importance(struct tipc_port *port, int imp)
{
+ if (imp > TIPC_CRITICAL_IMPORTANCE)
+ return -EINVAL;
msg_set_importance(&port->phdr, (u32)imp);
+ return 0;
}
#endif
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 7d423ee..ff8c811 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -1973,7 +1973,7 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt,
switch (opt) {
case TIPC_IMPORTANCE:
- tipc_port_set_importance(port, value);
+ res = tipc_port_set_importance(port, value);
break;
case TIPC_SRC_DROPPABLE:
if (sock->type != SOCK_STREAM)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 55ec9be..f52a4cd 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -630,6 +630,9 @@ int wiphy_register(struct wiphy *wiphy)
if (IS_ERR(rdev->wiphy.debugfsdir))
rdev->wiphy.debugfsdir = NULL;
+ cfg80211_debugfs_rdev_add(rdev);
+ nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
+
if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
struct regulatory_request request;
@@ -641,8 +644,6 @@ int wiphy_register(struct wiphy *wiphy)
nl80211_send_reg_change_event(&request);
}
- cfg80211_debugfs_rdev_add(rdev);
-
rdev->wiphy.registered = true;
rtnl_unlock();
@@ -654,8 +655,6 @@ int wiphy_register(struct wiphy *wiphy)
return res;
}
- nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
-
return 0;
}
EXPORT_SYMBOL(wiphy_register);