Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/padovan/bluetooth-next-2.6
This commit is contained in:
commit
1c1236e3af
@ -745,6 +745,33 @@ struct hci_cp_le_conn_update {
|
||||
__le16 max_ce_len;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_START_ENC 0x2019
|
||||
struct hci_cp_le_start_enc {
|
||||
__le16 handle;
|
||||
__u8 rand[8];
|
||||
__le16 ediv;
|
||||
__u8 ltk[16];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_LTK_REPLY 0x201a
|
||||
struct hci_cp_le_ltk_reply {
|
||||
__le16 handle;
|
||||
__u8 ltk[16];
|
||||
} __packed;
|
||||
struct hci_rp_le_ltk_reply {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_LTK_NEG_REPLY 0x201b
|
||||
struct hci_cp_le_ltk_neg_reply {
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
struct hci_rp_le_ltk_neg_reply {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
|
||||
/* ---- HCI Events ---- */
|
||||
#define HCI_EV_INQUIRY_COMPLETE 0x01
|
||||
|
||||
@ -1035,6 +1062,13 @@ struct hci_ev_le_conn_complete {
|
||||
__u8 clk_accurancy;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_LE_LTK_REQ 0x05
|
||||
struct hci_ev_le_ltk_req {
|
||||
__le16 handle;
|
||||
__u8 random[8];
|
||||
__le16 ediv;
|
||||
} __packed;
|
||||
|
||||
/* Advertising report event types */
|
||||
#define ADV_IND 0x00
|
||||
#define ADV_DIRECT_IND 0x01
|
||||
|
@ -177,6 +177,8 @@ struct hci_dev {
|
||||
|
||||
__u16 init_last_cmd;
|
||||
|
||||
struct crypto_blkcipher *tfm;
|
||||
|
||||
struct inquiry_cache inq_cache;
|
||||
struct hci_conn_hash conn_hash;
|
||||
struct list_head blacklist;
|
||||
@ -247,6 +249,7 @@ struct hci_conn {
|
||||
__u8 power_save;
|
||||
__u16 disc_timeout;
|
||||
unsigned long pend;
|
||||
__u8 ltk[16];
|
||||
|
||||
__u8 remote_cap;
|
||||
__u8 remote_oob;
|
||||
@ -526,6 +529,8 @@ int hci_inquiry(void __user *arg);
|
||||
|
||||
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
int hci_blacklist_clear(struct hci_dev *hdev);
|
||||
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
|
||||
int hci_uuids_clear(struct hci_dev *hdev);
|
||||
|
||||
@ -742,6 +747,9 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
||||
if (conn->sec_level == BT_SECURITY_SDP)
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
|
||||
if (conn->pending_sec_level > conn->sec_level)
|
||||
conn->sec_level = conn->pending_sec_level;
|
||||
|
||||
hci_proto_encrypt_cfm(conn, status, encrypt);
|
||||
|
||||
read_lock_bh(&hci_cb_list_lock);
|
||||
@ -859,4 +867,9 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result);
|
||||
|
||||
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
||||
u16 latency, u16 to_multiplier);
|
||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
|
||||
__u8 ltk[16]);
|
||||
void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]);
|
||||
void hci_le_ltk_neg_reply(struct hci_conn *conn);
|
||||
|
||||
#endif /* __HCI_CORE_H */
|
||||
|
@ -287,6 +287,10 @@ struct l2cap_chan {
|
||||
|
||||
struct l2cap_conn *conn;
|
||||
|
||||
__u8 state;
|
||||
|
||||
atomic_t refcnt;
|
||||
|
||||
__le16 psm;
|
||||
__u16 dcid;
|
||||
__u16 scid;
|
||||
@ -320,8 +324,8 @@ struct l2cap_chan {
|
||||
__u16 monitor_timeout;
|
||||
__u16 mps;
|
||||
|
||||
__u8 conf_state;
|
||||
__u16 conn_state;
|
||||
unsigned long conf_state;
|
||||
unsigned long conn_state;
|
||||
|
||||
__u8 next_tx_seq;
|
||||
__u8 expected_ack_seq;
|
||||
@ -354,6 +358,18 @@ struct l2cap_chan {
|
||||
|
||||
struct list_head list;
|
||||
struct list_head global_l;
|
||||
|
||||
void *data;
|
||||
struct l2cap_ops *ops;
|
||||
};
|
||||
|
||||
struct l2cap_ops {
|
||||
char *name;
|
||||
|
||||
struct l2cap_chan *(*new_connection) (void *data);
|
||||
int (*recv) (void *data, struct sk_buff *skb);
|
||||
void (*close) (void *data);
|
||||
void (*state_change) (void *data, int state);
|
||||
};
|
||||
|
||||
struct l2cap_conn {
|
||||
@ -379,6 +395,15 @@ struct l2cap_conn {
|
||||
|
||||
__u8 disc_reason;
|
||||
|
||||
__u8 preq[7]; /* SMP Pairing Request */
|
||||
__u8 prsp[7]; /* SMP Pairing Response */
|
||||
__u8 prnd[16]; /* SMP Pairing Random */
|
||||
__u8 pcnf[16]; /* SMP Pairing Confirm */
|
||||
__u8 tk[16]; /* SMP Temporary Key */
|
||||
__u8 smp_key_size;
|
||||
|
||||
struct timer_list security_timer;
|
||||
|
||||
struct list_head chan_l;
|
||||
rwlock_t chan_lock;
|
||||
};
|
||||
@ -399,36 +424,45 @@ struct l2cap_pinfo {
|
||||
struct l2cap_chan *chan;
|
||||
};
|
||||
|
||||
#define L2CAP_CONF_REQ_SENT 0x01
|
||||
#define L2CAP_CONF_INPUT_DONE 0x02
|
||||
#define L2CAP_CONF_OUTPUT_DONE 0x04
|
||||
#define L2CAP_CONF_MTU_DONE 0x08
|
||||
#define L2CAP_CONF_MODE_DONE 0x10
|
||||
#define L2CAP_CONF_CONNECT_PEND 0x20
|
||||
#define L2CAP_CONF_NO_FCS_RECV 0x40
|
||||
#define L2CAP_CONF_STATE2_DEVICE 0x80
|
||||
enum {
|
||||
CONF_REQ_SENT,
|
||||
CONF_INPUT_DONE,
|
||||
CONF_OUTPUT_DONE,
|
||||
CONF_MTU_DONE,
|
||||
CONF_MODE_DONE,
|
||||
CONF_CONNECT_PEND,
|
||||
CONF_NO_FCS_RECV,
|
||||
CONF_STATE2_DEVICE,
|
||||
};
|
||||
|
||||
#define L2CAP_CONF_MAX_CONF_REQ 2
|
||||
#define L2CAP_CONF_MAX_CONF_RSP 2
|
||||
|
||||
#define L2CAP_CONN_SAR_SDU 0x0001
|
||||
#define L2CAP_CONN_SREJ_SENT 0x0002
|
||||
#define L2CAP_CONN_WAIT_F 0x0004
|
||||
#define L2CAP_CONN_SREJ_ACT 0x0008
|
||||
#define L2CAP_CONN_SEND_PBIT 0x0010
|
||||
#define L2CAP_CONN_REMOTE_BUSY 0x0020
|
||||
#define L2CAP_CONN_LOCAL_BUSY 0x0040
|
||||
#define L2CAP_CONN_REJ_ACT 0x0080
|
||||
#define L2CAP_CONN_SEND_FBIT 0x0100
|
||||
#define L2CAP_CONN_RNR_SENT 0x0200
|
||||
#define L2CAP_CONN_SAR_RETRY 0x0400
|
||||
enum {
|
||||
CONN_SAR_SDU,
|
||||
CONN_SREJ_SENT,
|
||||
CONN_WAIT_F,
|
||||
CONN_SREJ_ACT,
|
||||
CONN_SEND_PBIT,
|
||||
CONN_REMOTE_BUSY,
|
||||
CONN_LOCAL_BUSY,
|
||||
CONN_REJ_ACT,
|
||||
CONN_SEND_FBIT,
|
||||
CONN_RNR_SENT,
|
||||
CONN_SAR_RETRY,
|
||||
};
|
||||
|
||||
#define __mod_retrans_timer() mod_timer(&chan->retrans_timer, \
|
||||
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_RETRANS_TO));
|
||||
#define __mod_monitor_timer() mod_timer(&chan->monitor_timer, \
|
||||
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_MONITOR_TO));
|
||||
#define __mod_ack_timer() mod_timer(&chan->ack_timer, \
|
||||
jiffies + msecs_to_jiffies(L2CAP_DEFAULT_ACK_TO));
|
||||
#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
|
||||
#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
|
||||
#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
|
||||
L2CAP_DEFAULT_RETRANS_TO);
|
||||
#define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer)
|
||||
#define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \
|
||||
L2CAP_DEFAULT_MONITOR_TO);
|
||||
#define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer)
|
||||
#define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \
|
||||
L2CAP_DEFAULT_ACK_TO);
|
||||
#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer)
|
||||
|
||||
static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
|
||||
{
|
||||
@ -459,11 +493,6 @@ int __l2cap_wait_ack(struct sock *sk);
|
||||
int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm);
|
||||
int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid);
|
||||
|
||||
void l2cap_sock_kill(struct sock *sk);
|
||||
void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
||||
struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
|
||||
int proto, gfp_t prio);
|
||||
|
||||
struct l2cap_chan *l2cap_chan_create(struct sock *sk);
|
||||
void l2cap_chan_close(struct l2cap_chan *chan, int reason);
|
||||
void l2cap_chan_destroy(struct l2cap_chan *chan);
|
||||
|
@ -199,6 +199,16 @@ struct mgmt_cp_remove_remote_oob_data {
|
||||
|
||||
#define MGMT_OP_STOP_DISCOVERY 0x001C
|
||||
|
||||
#define MGMT_OP_BLOCK_DEVICE 0x001D
|
||||
struct mgmt_cp_block_device {
|
||||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_UNBLOCK_DEVICE 0x001E
|
||||
struct mgmt_cp_unblock_device {
|
||||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||
struct mgmt_ev_cmd_complete {
|
||||
__le16 opcode;
|
||||
|
@ -1,3 +1,25 @@
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
#ifndef __SMP_H
|
||||
#define __SMP_H
|
||||
|
||||
@ -16,6 +38,23 @@ struct smp_cmd_pairing {
|
||||
__u8 resp_key_dist;
|
||||
} __packed;
|
||||
|
||||
#define SMP_IO_DISPLAY_ONLY 0x00
|
||||
#define SMP_IO_DISPLAY_YESNO 0x01
|
||||
#define SMP_IO_KEYBOARD_ONLY 0x02
|
||||
#define SMP_IO_NO_INPUT_OUTPUT 0x03
|
||||
#define SMP_IO_KEYBOARD_DISPLAY 0x04
|
||||
|
||||
#define SMP_OOB_NOT_PRESENT 0x00
|
||||
#define SMP_OOB_PRESENT 0x01
|
||||
|
||||
#define SMP_DIST_ENC_KEY 0x01
|
||||
#define SMP_DIST_ID_KEY 0x02
|
||||
#define SMP_DIST_SIGN 0x04
|
||||
|
||||
#define SMP_AUTH_NONE 0x00
|
||||
#define SMP_AUTH_BONDING 0x01
|
||||
#define SMP_AUTH_MITM 0x04
|
||||
|
||||
#define SMP_CMD_PAIRING_CONFIRM 0x03
|
||||
struct smp_cmd_pairing_confirm {
|
||||
__u8 confirm_val[16];
|
||||
@ -73,4 +112,11 @@ struct smp_cmd_security_req {
|
||||
#define SMP_UNSPECIFIED 0x08
|
||||
#define SMP_REPEATED_ATTEMPTS 0x09
|
||||
|
||||
#define SMP_MIN_ENC_KEY_SIZE 7
|
||||
#define SMP_MAX_ENC_KEY_SIZE 16
|
||||
|
||||
/* SMP Commands */
|
||||
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level);
|
||||
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
|
||||
|
||||
#endif /* __SMP_H */
|
||||
|
@ -22,6 +22,7 @@ menuconfig BT
|
||||
BNEP Module (Bluetooth Network Encapsulation Protocol)
|
||||
CMTP Module (CAPI Message Transport Protocol)
|
||||
HIDP Module (Human Interface Device Protocol)
|
||||
SMP Module (Security Manager Protocol)
|
||||
|
||||
Say Y here to compile Bluetooth support into the kernel or say M to
|
||||
compile it as module (bluetooth).
|
||||
@ -36,11 +37,18 @@ if BT != n
|
||||
config BT_L2CAP
|
||||
bool "L2CAP protocol support"
|
||||
select CRC16
|
||||
select CRYPTO
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_ECB
|
||||
help
|
||||
L2CAP (Logical Link Control and Adaptation Protocol) provides
|
||||
connection oriented and connection-less data transport. L2CAP
|
||||
support is required for most Bluetooth applications.
|
||||
|
||||
Also included is support for SMP (Security Manager Protocol) which
|
||||
is the security layer on top of LE (Low Energy) links.
|
||||
|
||||
config BT_SCO
|
||||
bool "SCO links support"
|
||||
help
|
||||
|
@ -9,5 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/
|
||||
obj-$(CONFIG_BT_HIDP) += hidp/
|
||||
|
||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o
|
||||
bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o
|
||||
bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o smp.o
|
||||
bluetooth-$(CONFIG_BT_SCO) += sco.o
|
||||
|
@ -53,6 +53,7 @@ static void hci_le_connect(struct hci_conn *conn)
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = 1;
|
||||
conn->link_mode |= HCI_LM_MASTER;
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.scan_interval = cpu_to_le16(0x0004);
|
||||
@ -204,6 +205,55 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
||||
}
|
||||
EXPORT_SYMBOL(hci_le_conn_update);
|
||||
|
||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
|
||||
__u8 ltk[16])
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_start_enc cp;
|
||||
|
||||
BT_DBG("%p", conn);
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
memcpy(cp.ltk, ltk, sizeof(cp.ltk));
|
||||
cp.ediv = ediv;
|
||||
memcpy(cp.rand, rand, sizeof(rand));
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_le_start_enc);
|
||||
|
||||
void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16])
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_ltk_reply cp;
|
||||
|
||||
BT_DBG("%p", conn);
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
memcpy(cp.ltk, ltk, sizeof(ltk));
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_le_ltk_reply);
|
||||
|
||||
void hci_le_ltk_neg_reply(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_ltk_neg_reply cp;
|
||||
|
||||
BT_DBG("%p", conn);
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
/* Device _must_ be locked */
|
||||
void hci_sco_setup(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
@ -620,10 +670,10 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
|
||||
goto encrypt;
|
||||
|
||||
auth:
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||
return 0;
|
||||
|
||||
hci_conn_auth(conn, sec_level, auth_type);
|
||||
if (!hci_conn_auth(conn, sec_level, auth_type))
|
||||
return 0;
|
||||
|
||||
encrypt:
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
@ -59,6 +60,8 @@ static void hci_tx_task(unsigned long arg);
|
||||
|
||||
static DEFINE_RWLOCK(hci_task_lock);
|
||||
|
||||
static int enable_smp;
|
||||
|
||||
/* HCI device list */
|
||||
LIST_HEAD(hci_dev_list);
|
||||
DEFINE_RWLOCK(hci_dev_list_lock);
|
||||
@ -1202,6 +1205,97 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(p, struct bdaddr_list, list);
|
||||
|
||||
if (bacmp(bdaddr, &b->bdaddr) == 0)
|
||||
return b;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int hci_blacklist_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
list_for_each_safe(p, n, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(p, struct bdaddr_list, list);
|
||||
|
||||
list_del(p);
|
||||
kfree(b);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct bdaddr_list *entry;
|
||||
int err;
|
||||
|
||||
if (bacmp(bdaddr, BDADDR_ANY) == 0)
|
||||
return -EBADF;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (hci_blacklist_lookup(hdev, bdaddr)) {
|
||||
err = -EEXIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
return -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
bacpy(&entry->bdaddr, bdaddr);
|
||||
|
||||
list_add(&entry->list, &hdev->blacklist);
|
||||
|
||||
err = 0;
|
||||
|
||||
err:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct bdaddr_list *entry;
|
||||
int err = 0;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (bacmp(bdaddr, BDADDR_ANY) == 0) {
|
||||
hci_blacklist_clear(hdev);
|
||||
goto done;
|
||||
}
|
||||
|
||||
entry = hci_blacklist_lookup(hdev, bdaddr);
|
||||
if (!entry) {
|
||||
err = -ENOENT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hci_clear_adv_cache(unsigned long arg)
|
||||
{
|
||||
struct hci_dev *hdev = (void *) arg;
|
||||
@ -1274,6 +1368,14 @@ int hci_add_adv_entry(struct hci_dev *hdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct crypto_blkcipher *alloc_cypher(void)
|
||||
{
|
||||
if (enable_smp)
|
||||
return crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
/* Register HCI device */
|
||||
int hci_register_dev(struct hci_dev *hdev)
|
||||
{
|
||||
@ -1358,6 +1460,11 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
if (!hdev->workqueue)
|
||||
goto nomem;
|
||||
|
||||
hdev->tfm = alloc_cypher();
|
||||
if (IS_ERR(hdev->tfm))
|
||||
BT_INFO("Failed to load transform for ecb(aes): %ld",
|
||||
PTR_ERR(hdev->tfm));
|
||||
|
||||
hci_register_sysfs(hdev);
|
||||
|
||||
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
|
||||
@ -1406,6 +1513,9 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
||||
!test_bit(HCI_SETUP, &hdev->flags))
|
||||
mgmt_index_removed(hdev->id);
|
||||
|
||||
if (!IS_ERR(hdev->tfm))
|
||||
crypto_free_blkcipher(hdev->tfm);
|
||||
|
||||
hci_notify(hdev, HCI_DEV_UNREG);
|
||||
|
||||
if (hdev->rfkill) {
|
||||
@ -2242,3 +2352,6 @@ static void hci_cmd_task(unsigned long arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module_param(enable_smp, bool, 0644);
|
||||
MODULE_PARM_DESC(enable_smp, "Enable SMP support (LE only)");
|
||||
|
@ -868,6 +868,30 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
|
||||
}
|
||||
|
||||
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
@ -1248,6 +1272,11 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
}
|
||||
|
||||
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
@ -1593,6 +1622,7 @@ static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *
|
||||
/* Encryption implies authentication */
|
||||
conn->link_mode |= HCI_LM_AUTH;
|
||||
conn->link_mode |= HCI_LM_ENCRYPT;
|
||||
conn->sec_level = conn->pending_sec_level;
|
||||
} else
|
||||
conn->link_mode &= ~HCI_LM_ENCRYPT;
|
||||
}
|
||||
@ -1856,6 +1886,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
|
||||
hci_cc_le_set_scan_enable(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_LTK_REPLY:
|
||||
hci_cc_le_ltk_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_LTK_NEG_REPLY:
|
||||
hci_cc_le_ltk_neg_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||
break;
|
||||
@ -1934,6 +1972,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_cs_le_create_conn(hdev, ev->status);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_START_ENC:
|
||||
hci_cs_le_start_enc(hdev, ev->status);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||
break;
|
||||
@ -2712,6 +2754,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
|
||||
|
||||
mgmt_connected(hdev->id, &ev->bdaddr);
|
||||
|
||||
conn->sec_level = BT_SECURITY_LOW;
|
||||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
@ -2745,6 +2788,28 @@ static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_ltk_req *ev = (void *) skb->data;
|
||||
struct hci_cp_le_ltk_reply cp;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk));
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_meta *le_ev = (void *) skb->data;
|
||||
@ -2760,6 +2825,10 @@ static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_le_adv_report_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_LE_LTK_REQ:
|
||||
hci_le_ltk_request_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -180,82 +180,24 @@ static int hci_sock_release(struct socket *sock)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(p, struct bdaddr_list, list);
|
||||
|
||||
if (bacmp(bdaddr, &b->bdaddr) == 0)
|
||||
return b;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg)
|
||||
static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg)
|
||||
{
|
||||
bdaddr_t bdaddr;
|
||||
struct bdaddr_list *entry;
|
||||
|
||||
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
||||
return -EFAULT;
|
||||
|
||||
if (bacmp(&bdaddr, BDADDR_ANY) == 0)
|
||||
return -EBADF;
|
||||
|
||||
if (hci_blacklist_lookup(hdev, &bdaddr))
|
||||
return -EEXIST;
|
||||
|
||||
entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
bacpy(&entry->bdaddr, &bdaddr);
|
||||
|
||||
list_add(&entry->list, &hdev->blacklist);
|
||||
|
||||
return 0;
|
||||
return hci_blacklist_add(hdev, &bdaddr);
|
||||
}
|
||||
|
||||
int hci_blacklist_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
list_for_each_safe(p, n, &hdev->blacklist) {
|
||||
struct bdaddr_list *b;
|
||||
|
||||
b = list_entry(p, struct bdaddr_list, list);
|
||||
|
||||
list_del(p);
|
||||
kfree(b);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hci_blacklist_del(struct hci_dev *hdev, void __user *arg)
|
||||
static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg)
|
||||
{
|
||||
bdaddr_t bdaddr;
|
||||
struct bdaddr_list *entry;
|
||||
|
||||
if (copy_from_user(&bdaddr, arg, sizeof(bdaddr)))
|
||||
return -EFAULT;
|
||||
|
||||
if (bacmp(&bdaddr, BDADDR_ANY) == 0)
|
||||
return hci_blacklist_clear(hdev);
|
||||
|
||||
entry = hci_blacklist_lookup(hdev, &bdaddr);
|
||||
if (!entry)
|
||||
return -ENOENT;
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
|
||||
return 0;
|
||||
return hci_blacklist_del(hdev, &bdaddr);
|
||||
}
|
||||
|
||||
/* Ioctls that require bound socket */
|
||||
@ -290,12 +232,12 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign
|
||||
case HCIBLOCKADDR:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EACCES;
|
||||
return hci_blacklist_add(hdev, (void __user *) arg);
|
||||
return hci_sock_blacklist_add(hdev, (void __user *) arg);
|
||||
|
||||
case HCIUNBLOCKADDR:
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EACCES;
|
||||
return hci_blacklist_del(hdev, (void __user *) arg);
|
||||
return hci_sock_blacklist_del(hdev, (void __user *) arg);
|
||||
|
||||
default:
|
||||
if (hdev->ioctl)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -29,8 +29,11 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
#include <net/bluetooth/smp.h>
|
||||
|
||||
static const struct proto_ops l2cap_sock_ops;
|
||||
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio);
|
||||
|
||||
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
||||
{
|
||||
@ -87,6 +90,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
||||
chan->sec_level = BT_SECURITY_SDP;
|
||||
|
||||
bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
|
||||
|
||||
chan->state = BT_BOUND;
|
||||
sk->sk_state = BT_BOUND;
|
||||
|
||||
done:
|
||||
@ -212,6 +217,8 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
|
||||
|
||||
sk->sk_max_ack_backlog = backlog;
|
||||
sk->sk_ack_backlog = 0;
|
||||
|
||||
chan->state = BT_LISTEN;
|
||||
sk->sk_state = BT_LISTEN;
|
||||
|
||||
done:
|
||||
@ -505,7 +512,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
|
||||
chan->mode = opts.mode;
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
chan->conf_state &= ~L2CAP_CONF_STATE2_DEVICE;
|
||||
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
|
||||
break;
|
||||
case L2CAP_MODE_ERTM:
|
||||
case L2CAP_MODE_STREAMING:
|
||||
@ -556,6 +563,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
struct bt_security sec;
|
||||
struct bt_power pwr;
|
||||
struct l2cap_conn *conn;
|
||||
int len, err = 0;
|
||||
u32 opt;
|
||||
|
||||
@ -592,6 +600,20 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
||||
}
|
||||
|
||||
chan->sec_level = sec.level;
|
||||
|
||||
conn = chan->conn;
|
||||
if (conn && chan->scid == L2CAP_CID_LE_DATA) {
|
||||
if (!conn->hcon->out) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (smp_conn_security(conn, sec.level))
|
||||
break;
|
||||
|
||||
err = 0;
|
||||
sk->sk_state = BT_CONFIG;
|
||||
}
|
||||
break;
|
||||
|
||||
case BT_DEFER_SETUP:
|
||||
@ -711,7 +733,7 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
||||
/* Kill socket (only if zapped and orphan)
|
||||
* Must be called on unlocked socket.
|
||||
*/
|
||||
void l2cap_sock_kill(struct sock *sk)
|
||||
static void l2cap_sock_kill(struct sock *sk)
|
||||
{
|
||||
if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket)
|
||||
return;
|
||||
@ -773,6 +795,49 @@ static int l2cap_sock_release(struct socket *sock)
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data)
|
||||
{
|
||||
struct sock *sk, *parent = data;
|
||||
|
||||
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
|
||||
GFP_ATOMIC);
|
||||
if (!sk)
|
||||
return NULL;
|
||||
|
||||
l2cap_sock_init(sk, parent);
|
||||
|
||||
return l2cap_pi(sk)->chan;
|
||||
}
|
||||
|
||||
static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
|
||||
{
|
||||
struct sock *sk = data;
|
||||
|
||||
return sock_queue_rcv_skb(sk, skb);
|
||||
}
|
||||
|
||||
static void l2cap_sock_close_cb(void *data)
|
||||
{
|
||||
struct sock *sk = data;
|
||||
|
||||
l2cap_sock_kill(sk);
|
||||
}
|
||||
|
||||
static void l2cap_sock_state_change_cb(void *data, int state)
|
||||
{
|
||||
struct sock *sk = data;
|
||||
|
||||
sk->sk_state = state;
|
||||
}
|
||||
|
||||
static struct l2cap_ops l2cap_chan_ops = {
|
||||
.name = "L2CAP Socket Interface",
|
||||
.new_connection = l2cap_sock_new_connection_cb,
|
||||
.recv = l2cap_sock_recv_cb,
|
||||
.close = l2cap_sock_close_cb,
|
||||
.state_change = l2cap_sock_state_change_cb,
|
||||
};
|
||||
|
||||
static void l2cap_sock_destruct(struct sock *sk)
|
||||
{
|
||||
BT_DBG("sk %p", sk);
|
||||
@ -781,7 +846,7 @@ static void l2cap_sock_destruct(struct sock *sk)
|
||||
skb_queue_purge(&sk->sk_write_queue);
|
||||
}
|
||||
|
||||
void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
{
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
struct l2cap_chan *chan = pi->chan;
|
||||
@ -826,7 +891,7 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
chan->omtu = 0;
|
||||
if (!disable_ertm && sk->sk_type == SOCK_STREAM) {
|
||||
chan->mode = L2CAP_MODE_ERTM;
|
||||
chan->conf_state |= L2CAP_CONF_STATE2_DEVICE;
|
||||
set_bit(CONF_STATE2_DEVICE, &chan->conf_state);
|
||||
} else {
|
||||
chan->mode = L2CAP_MODE_BASIC;
|
||||
}
|
||||
@ -838,10 +903,14 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
||||
chan->force_reliable = 0;
|
||||
chan->flushable = BT_FLUSHABLE_OFF;
|
||||
chan->force_active = BT_POWER_FORCE_ACTIVE_ON;
|
||||
|
||||
}
|
||||
|
||||
/* Default config options */
|
||||
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
|
||||
|
||||
chan->data = sk;
|
||||
chan->ops = &l2cap_chan_ops;
|
||||
}
|
||||
|
||||
static struct proto l2cap_proto = {
|
||||
@ -850,9 +919,10 @@ static struct proto l2cap_proto = {
|
||||
.obj_size = sizeof(struct l2cap_pinfo)
|
||||
};
|
||||
|
||||
struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
|
||||
static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto);
|
||||
if (!sk)
|
||||
@ -869,6 +939,14 @@ struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, g
|
||||
sk->sk_protocol = proto;
|
||||
sk->sk_state = BT_OPEN;
|
||||
|
||||
chan = l2cap_chan_create(sk);
|
||||
if (!chan) {
|
||||
l2cap_sock_kill(sk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
l2cap_pi(sk)->chan = chan;
|
||||
|
||||
return sk;
|
||||
}
|
||||
|
||||
@ -876,7 +954,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
int kern)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct l2cap_chan *chan;
|
||||
|
||||
BT_DBG("sock %p", sock);
|
||||
|
||||
@ -895,14 +972,6 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
|
||||
if (!sk)
|
||||
return -ENOMEM;
|
||||
|
||||
chan = l2cap_chan_create(sk);
|
||||
if (!chan) {
|
||||
l2cap_sock_kill(sk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
l2cap_pi(sk)->chan = chan;
|
||||
|
||||
l2cap_sock_init(sk, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
@ -990,7 +990,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
||||
|
||||
put_unaligned_le16(conn->handle, &dc.handle);
|
||||
dc.reason = 0x13; /* Remote User Terminated Connection */
|
||||
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL);
|
||||
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
|
||||
}
|
||||
|
||||
unlock:
|
||||
@ -1666,6 +1666,70 @@ failed:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int block_device(struct sock *sk, u16 index, unsigned char *data,
|
||||
u16 len)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct mgmt_cp_block_device *cp;
|
||||
int err;
|
||||
|
||||
BT_DBG("hci%u", index);
|
||||
|
||||
cp = (void *) data;
|
||||
|
||||
if (len != sizeof(*cp))
|
||||
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||
EINVAL);
|
||||
|
||||
hdev = hci_dev_get(index);
|
||||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||
ENODEV);
|
||||
|
||||
err = hci_blacklist_add(hdev, &cp->bdaddr);
|
||||
|
||||
if (err < 0)
|
||||
err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err);
|
||||
else
|
||||
err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
|
||||
NULL, 0);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
|
||||
u16 len)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
struct mgmt_cp_unblock_device *cp;
|
||||
int err;
|
||||
|
||||
BT_DBG("hci%u", index);
|
||||
|
||||
cp = (void *) data;
|
||||
|
||||
if (len != sizeof(*cp))
|
||||
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||
EINVAL);
|
||||
|
||||
hdev = hci_dev_get(index);
|
||||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||
ENODEV);
|
||||
|
||||
err = hci_blacklist_del(hdev, &cp->bdaddr);
|
||||
|
||||
if (err < 0)
|
||||
err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err);
|
||||
else
|
||||
err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
|
||||
NULL, 0);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
||||
{
|
||||
unsigned char *buf;
|
||||
@ -1780,6 +1844,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
||||
case MGMT_OP_STOP_DISCOVERY:
|
||||
err = stop_discovery(sk, index);
|
||||
break;
|
||||
case MGMT_OP_BLOCK_DEVICE:
|
||||
err = block_device(sk, index, buf + sizeof(*hdr), len);
|
||||
break;
|
||||
case MGMT_OP_UNBLOCK_DEVICE:
|
||||
err = unblock_device(sk, index, buf + sizeof(*hdr), len);
|
||||
break;
|
||||
default:
|
||||
BT_DBG("Unknown op %u", opcode);
|
||||
err = cmd_status(sk, index, opcode, 0x01);
|
||||
|
533
net/bluetooth/smp.c
Normal file
533
net/bluetooth/smp.c
Normal file
@ -0,0 +1,533 @@
|
||||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
#include <net/bluetooth/smp.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/b128ops.h>
|
||||
|
||||
#define SMP_TIMEOUT 30000 /* 30 seconds */
|
||||
|
||||
static inline void swap128(u8 src[16], u8 dst[16])
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 16; i++)
|
||||
dst[15 - i] = src[i];
|
||||
}
|
||||
|
||||
static inline void swap56(u8 src[7], u8 dst[7])
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 7; i++)
|
||||
dst[6 - i] = src[i];
|
||||
}
|
||||
|
||||
static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
|
||||
{
|
||||
struct blkcipher_desc desc;
|
||||
struct scatterlist sg;
|
||||
int err, iv_len;
|
||||
unsigned char iv[128];
|
||||
|
||||
if (tfm == NULL) {
|
||||
BT_ERR("tfm %p", tfm);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc.tfm = tfm;
|
||||
desc.flags = 0;
|
||||
|
||||
err = crypto_blkcipher_setkey(tfm, k, 16);
|
||||
if (err) {
|
||||
BT_ERR("cipher setkey failed: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
sg_init_one(&sg, r, 16);
|
||||
|
||||
iv_len = crypto_blkcipher_ivsize(tfm);
|
||||
if (iv_len) {
|
||||
memset(&iv, 0xff, iv_len);
|
||||
crypto_blkcipher_set_iv(tfm, iv, iv_len);
|
||||
}
|
||||
|
||||
err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
|
||||
if (err)
|
||||
BT_ERR("Encrypt data error %d", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
|
||||
u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
|
||||
u8 _rat, bdaddr_t *ra, u8 res[16])
|
||||
{
|
||||
u8 p1[16], p2[16];
|
||||
int err;
|
||||
|
||||
memset(p1, 0, 16);
|
||||
|
||||
/* p1 = pres || preq || _rat || _iat */
|
||||
swap56(pres, p1);
|
||||
swap56(preq, p1 + 7);
|
||||
p1[14] = _rat;
|
||||
p1[15] = _iat;
|
||||
|
||||
memset(p2, 0, 16);
|
||||
|
||||
/* p2 = padding || ia || ra */
|
||||
baswap((bdaddr_t *) (p2 + 4), ia);
|
||||
baswap((bdaddr_t *) (p2 + 10), ra);
|
||||
|
||||
/* res = r XOR p1 */
|
||||
u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
|
||||
|
||||
/* res = e(k, res) */
|
||||
err = smp_e(tfm, k, res);
|
||||
if (err) {
|
||||
BT_ERR("Encrypt data error");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* res = res XOR p2 */
|
||||
u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
|
||||
|
||||
/* res = e(k, res) */
|
||||
err = smp_e(tfm, k, res);
|
||||
if (err)
|
||||
BT_ERR("Encrypt data error");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16],
|
||||
u8 r1[16], u8 r2[16], u8 _r[16])
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Just least significant octets from r1 and r2 are considered */
|
||||
memcpy(_r, r1 + 8, 8);
|
||||
memcpy(_r + 8, r2 + 8, 8);
|
||||
|
||||
err = smp_e(tfm, k, _r);
|
||||
if (err)
|
||||
BT_ERR("Encrypt data error");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int smp_rand(u8 *buf)
|
||||
{
|
||||
get_random_bytes(buf, 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
|
||||
u16 dlen, void *data)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct l2cap_hdr *lh;
|
||||
int len;
|
||||
|
||||
len = L2CAP_HDR_SIZE + sizeof(code) + dlen;
|
||||
|
||||
if (len > conn->mtu)
|
||||
return NULL;
|
||||
|
||||
skb = bt_skb_alloc(len, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
|
||||
lh->len = cpu_to_le16(sizeof(code) + dlen);
|
||||
lh->cid = cpu_to_le16(L2CAP_CID_SMP);
|
||||
|
||||
memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
|
||||
|
||||
memcpy(skb_put(skb, dlen), data, dlen);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
|
||||
{
|
||||
struct sk_buff *skb = smp_build_cmd(conn, code, len, data);
|
||||
|
||||
BT_DBG("code 0x%2.2x", code);
|
||||
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
hci_send_acl(conn->hcon, skb, 0);
|
||||
}
|
||||
|
||||
static __u8 seclevel_to_authreq(__u8 level)
|
||||
{
|
||||
switch (level) {
|
||||
case BT_SECURITY_HIGH:
|
||||
/* Right now we don't support bonding */
|
||||
return SMP_AUTH_MITM;
|
||||
|
||||
default:
|
||||
return SMP_AUTH_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
struct smp_cmd_pairing *cmd, __u8 authreq)
|
||||
{
|
||||
cmd->io_capability = conn->hcon->io_capability;
|
||||
cmd->oob_flag = SMP_OOB_NOT_PRESENT;
|
||||
cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
cmd->init_key_dist = 0x00;
|
||||
cmd->resp_key_dist = 0x00;
|
||||
cmd->auth_req = authreq;
|
||||
}
|
||||
|
||||
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
|
||||
{
|
||||
if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
|
||||
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
conn->smp_key_size = max_key_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
|
||||
u8 key_size;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||
memcpy(&conn->preq[1], req, sizeof(*req));
|
||||
skb_pull(skb, sizeof(*req));
|
||||
|
||||
if (req->oob_flag)
|
||||
return SMP_OOB_NOT_AVAIL;
|
||||
|
||||
/* We didn't start the pairing, so no requirements */
|
||||
build_pairing_cmd(conn, &rsp, SMP_AUTH_NONE);
|
||||
|
||||
key_size = min(req->max_key_size, rsp.max_key_size);
|
||||
if (check_enc_key_size(conn, key_size))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
/* Just works */
|
||||
memset(conn->tk, 0, sizeof(conn->tk));
|
||||
|
||||
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||
memcpy(&conn->prsp[1], &rsp, sizeof(rsp));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_cmd_pairing_confirm cp;
|
||||
struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
|
||||
int ret;
|
||||
u8 res[16], key_size;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
skb_pull(skb, sizeof(*rsp));
|
||||
|
||||
req = (void *) &conn->preq[1];
|
||||
|
||||
key_size = min(req->max_key_size, rsp->max_key_size);
|
||||
if (check_enc_key_size(conn, key_size))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
if (rsp->oob_flag)
|
||||
return SMP_OOB_NOT_AVAIL;
|
||||
|
||||
/* Just works */
|
||||
memset(conn->tk, 0, sizeof(conn->tk));
|
||||
|
||||
conn->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||
memcpy(&conn->prsp[1], rsp, sizeof(*rsp));
|
||||
|
||||
ret = smp_rand(conn->prnd);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0,
|
||||
conn->src, conn->hcon->dst_type, conn->dst, res);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
swap128(res, cp.confirm_val);
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm;
|
||||
|
||||
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
|
||||
|
||||
memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf));
|
||||
skb_pull(skb, sizeof(conn->pcnf));
|
||||
|
||||
if (conn->hcon->out) {
|
||||
u8 random[16];
|
||||
|
||||
swap128(conn->prnd, random);
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
|
||||
random);
|
||||
} else {
|
||||
struct smp_cmd_pairing_confirm cp;
|
||||
int ret;
|
||||
u8 res[16];
|
||||
|
||||
ret = smp_rand(conn->prnd);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp,
|
||||
conn->hcon->dst_type, conn->dst,
|
||||
0, conn->src, res);
|
||||
if (ret)
|
||||
return SMP_CONFIRM_FAILED;
|
||||
|
||||
swap128(res, cp.confirm_val);
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
struct crypto_blkcipher *tfm = hcon->hdev->tfm;
|
||||
int ret;
|
||||
u8 key[16], res[16], random[16], confirm[16];
|
||||
|
||||
swap128(skb->data, random);
|
||||
skb_pull(skb, sizeof(random));
|
||||
|
||||
memset(hcon->ltk, 0, sizeof(hcon->ltk));
|
||||
|
||||
if (conn->hcon->out)
|
||||
ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0,
|
||||
conn->src, conn->hcon->dst_type, conn->dst,
|
||||
res);
|
||||
else
|
||||
ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp,
|
||||
conn->hcon->dst_type, conn->dst, 0, conn->src,
|
||||
res);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
|
||||
|
||||
swap128(res, confirm);
|
||||
|
||||
if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) {
|
||||
BT_ERR("Pairing failed (confirmation values mismatch)");
|
||||
return SMP_CONFIRM_FAILED;
|
||||
}
|
||||
|
||||
if (conn->hcon->out) {
|
||||
__le16 ediv;
|
||||
u8 rand[8];
|
||||
|
||||
smp_s1(tfm, conn->tk, random, conn->prnd, key);
|
||||
swap128(key, hcon->ltk);
|
||||
|
||||
memset(hcon->ltk + conn->smp_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
||||
|
||||
memset(rand, 0, sizeof(rand));
|
||||
ediv = 0;
|
||||
hci_le_start_enc(hcon, ediv, rand, hcon->ltk);
|
||||
} else {
|
||||
u8 r[16];
|
||||
|
||||
swap128(conn->prnd, r);
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
|
||||
|
||||
smp_s1(tfm, conn->tk, conn->prnd, random, key);
|
||||
swap128(key, hcon->ltk);
|
||||
|
||||
memset(hcon->ltk + conn->smp_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct smp_cmd_security_req *rp = (void *) skb->data;
|
||||
struct smp_cmd_pairing cp;
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
|
||||
return 0;
|
||||
|
||||
skb_pull(skb, sizeof(*rp));
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
build_pairing_cmd(conn, &cp, rp->auth_req);
|
||||
|
||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
|
||||
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
||||
{
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
__u8 authreq;
|
||||
|
||||
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
|
||||
|
||||
if (IS_ERR(hcon->hdev->tfm))
|
||||
return 1;
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend))
|
||||
return 0;
|
||||
|
||||
if (sec_level == BT_SECURITY_LOW)
|
||||
return 1;
|
||||
|
||||
if (hcon->sec_level >= sec_level)
|
||||
return 1;
|
||||
|
||||
authreq = seclevel_to_authreq(sec_level);
|
||||
|
||||
if (hcon->link_mode & HCI_LM_MASTER) {
|
||||
struct smp_cmd_pairing cp;
|
||||
|
||||
build_pairing_cmd(conn, &cp, authreq);
|
||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
||||
} else {
|
||||
struct smp_cmd_security_req cp;
|
||||
cp.auth_req = authreq;
|
||||
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
hcon->pending_sec_level = sec_level;
|
||||
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
__u8 code = skb->data[0];
|
||||
__u8 reason;
|
||||
int err = 0;
|
||||
|
||||
if (IS_ERR(conn->hcon->hdev->tfm)) {
|
||||
err = PTR_ERR(conn->hcon->hdev->tfm);
|
||||
reason = SMP_PAIRING_NOTSUPP;
|
||||
goto done;
|
||||
}
|
||||
|
||||
skb_pull(skb, sizeof(code));
|
||||
|
||||
switch (code) {
|
||||
case SMP_CMD_PAIRING_REQ:
|
||||
reason = smp_cmd_pairing_req(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_PAIRING_FAIL:
|
||||
reason = 0;
|
||||
err = -EPERM;
|
||||
break;
|
||||
|
||||
case SMP_CMD_PAIRING_RSP:
|
||||
reason = smp_cmd_pairing_rsp(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_SECURITY_REQ:
|
||||
reason = smp_cmd_security_req(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_PAIRING_CONFIRM:
|
||||
reason = smp_cmd_pairing_confirm(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_PAIRING_RANDOM:
|
||||
reason = smp_cmd_pairing_random(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_ENCRYPT_INFO:
|
||||
case SMP_CMD_MASTER_IDENT:
|
||||
case SMP_CMD_IDENT_INFO:
|
||||
case SMP_CMD_IDENT_ADDR_INFO:
|
||||
case SMP_CMD_SIGN_INFO:
|
||||
default:
|
||||
BT_DBG("Unknown command code 0x%2.2x", code);
|
||||
|
||||
reason = SMP_CMD_NOTSUPP;
|
||||
err = -EOPNOTSUPP;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (reason)
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
|
||||
&reason);
|
||||
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
Loading…
Reference in New Issue
Block a user